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 "RenderTableSection.h" 28 #include "CachedImage.h" 29 #include "Document.h" 30 #include "HitTestResult.h" 31 #include "HTMLNames.h" 32 #include "PaintInfo.h" 33 #include "RenderTableCell.h" 34 #include "RenderTableCol.h" 35 #include "RenderTableRow.h" 36 #include "RenderView.h" 37 #include <limits> 38 #include <wtf/HashSet.h> 39 #include <wtf/Vector.h> 40 #ifdef ANDROID_LAYOUT 41 #include "Frame.h" 42 #include "Settings.h" 43 #endif 44 45 using namespace std; 46 47 namespace WebCore { 48 49 using namespace HTMLNames; 50 51 static inline void setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(RenderTableSection::RowStruct* row) 52 { 53 ASSERT(row && row->rowRenderer); 54 row->logicalHeight = row->rowRenderer->style()->logicalHeight(); 55 if (row->logicalHeight.isRelative()) 56 row->logicalHeight = Length(); 57 } 58 59 RenderTableSection::RenderTableSection(Node* node) 60 : RenderBox(node) 61 , m_gridRows(0) 62 , m_cCol(0) 63 , m_cRow(-1) 64 , m_outerBorderStart(0) 65 , m_outerBorderEnd(0) 66 , m_outerBorderBefore(0) 67 , m_outerBorderAfter(0) 68 , m_needsCellRecalc(false) 69 , m_hasOverflowingCell(false) 70 , m_hasMultipleCellLevels(false) 71 { 72 // init RenderObject attributes 73 setInline(false); // our object is not Inline 74 } 75 76 RenderTableSection::~RenderTableSection() 77 { 78 clearGrid(); 79 } 80 81 void RenderTableSection::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 82 { 83 RenderBox::styleDidChange(diff, oldStyle); 84 propagateStyleToAnonymousChildren(); 85 } 86 87 void RenderTableSection::destroy() 88 { 89 RenderTable* recalcTable = table(); 90 91 RenderBox::destroy(); 92 93 // recalc cell info because RenderTable has unguarded pointers 94 // stored that point to this RenderTableSection. 95 if (recalcTable) 96 recalcTable->setNeedsSectionRecalc(); 97 } 98 99 void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild) 100 { 101 // Make sure we don't append things after :after-generated content if we have it. 102 if (!beforeChild && isAfterContent(lastChild())) 103 beforeChild = lastChild(); 104 105 if (!child->isTableRow()) { 106 RenderObject* last = beforeChild; 107 if (!last) 108 last = lastChild(); 109 if (last && last->isAnonymous() && !last->isBeforeOrAfterContent()) { 110 if (beforeChild == last) 111 beforeChild = last->firstChild(); 112 last->addChild(child, beforeChild); 113 return; 114 } 115 116 // If beforeChild is inside an anonymous cell/row, insert into the cell or into 117 // the anonymous row containing it, if there is one. 118 RenderObject* lastBox = last; 119 while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow()) 120 lastBox = lastBox->parent(); 121 if (lastBox && lastBox->isAnonymous() && !lastBox->isBeforeOrAfterContent()) { 122 lastBox->addChild(child, beforeChild); 123 return; 124 } 125 126 RenderObject* row = new (renderArena()) RenderTableRow(document() /* anonymous table row */); 127 RefPtr<RenderStyle> newStyle = RenderStyle::create(); 128 newStyle->inheritFrom(style()); 129 newStyle->setDisplay(TABLE_ROW); 130 row->setStyle(newStyle.release()); 131 addChild(row, beforeChild); 132 row->addChild(child); 133 return; 134 } 135 136 if (beforeChild) 137 setNeedsCellRecalc(); 138 139 ++m_cRow; 140 m_cCol = 0; 141 142 // make sure we have enough rows 143 if (!ensureRows(m_cRow + 1)) 144 return; 145 146 m_grid[m_cRow].rowRenderer = toRenderTableRow(child); 147 148 if (!beforeChild) 149 setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(&m_grid[m_cRow]); 150 151 // If the next renderer is actually wrapped in an anonymous table row, we need to go up and find that. 152 while (beforeChild && beforeChild->parent() != this) 153 beforeChild = beforeChild->parent(); 154 155 ASSERT(!beforeChild || beforeChild->isTableRow()); 156 RenderBox::addChild(child, beforeChild); 157 toRenderTableRow(child)->updateBeforeAndAfterContent(); 158 } 159 160 void RenderTableSection::removeChild(RenderObject* oldChild) 161 { 162 setNeedsCellRecalc(); 163 RenderBox::removeChild(oldChild); 164 } 165 166 bool RenderTableSection::ensureRows(int numRows) 167 { 168 int nRows = m_gridRows; 169 if (numRows > nRows) { 170 if (numRows > static_cast<int>(m_grid.size())) { 171 size_t maxSize = numeric_limits<size_t>::max() / sizeof(RowStruct); 172 if (static_cast<size_t>(numRows) > maxSize) 173 return false; 174 m_grid.grow(numRows); 175 } 176 m_gridRows = numRows; 177 int nCols = max(1, table()->numEffCols()); 178 for (int r = nRows; r < numRows; r++) { 179 m_grid[r].row = new Row(nCols); 180 m_grid[r].rowRenderer = 0; 181 m_grid[r].baseline = 0; 182 m_grid[r].logicalHeight = Length(); 183 } 184 } 185 186 return true; 187 } 188 189 void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row) 190 { 191 int rSpan = cell->rowSpan(); 192 int cSpan = cell->colSpan(); 193 Vector<RenderTable::ColumnStruct>& columns = table()->columns(); 194 int nCols = columns.size(); 195 196 // ### mozilla still seems to do the old HTML way, even for strict DTD 197 // (see the annotation on table cell layouting in the CSS specs and the testcase below: 198 // <TABLE border> 199 // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4 200 // <TR><TD colspan="2">5 201 // </TABLE> 202 while (m_cCol < nCols && (cellAt(m_cRow, m_cCol).hasCells() || cellAt(m_cRow, m_cCol).inColSpan)) 203 m_cCol++; 204 205 if (rSpan == 1) { 206 // we ignore height settings on rowspan cells 207 Length logicalHeight = cell->style()->logicalHeight(); 208 if (logicalHeight.isPositive() || (logicalHeight.isRelative() && logicalHeight.value() >= 0)) { 209 Length cRowLogicalHeight = m_grid[m_cRow].logicalHeight; 210 switch (logicalHeight.type()) { 211 case Percent: 212 if (!(cRowLogicalHeight.isPercent()) || 213 (cRowLogicalHeight.isPercent() && cRowLogicalHeight.percent() < logicalHeight.percent())) 214 m_grid[m_cRow].logicalHeight = logicalHeight; 215 break; 216 case Fixed: 217 if (cRowLogicalHeight.type() < Percent || 218 (cRowLogicalHeight.isFixed() && cRowLogicalHeight.value() < logicalHeight.value())) 219 m_grid[m_cRow].logicalHeight = logicalHeight; 220 break; 221 case Relative: 222 default: 223 break; 224 } 225 } 226 } 227 228 // make sure we have enough rows 229 if (!ensureRows(m_cRow + rSpan)) 230 return; 231 232 m_grid[m_cRow].rowRenderer = row; 233 234 int col = m_cCol; 235 // tell the cell where it is 236 bool inColSpan = false; 237 while (cSpan) { 238 int currentSpan; 239 if (m_cCol >= nCols) { 240 table()->appendColumn(cSpan); 241 currentSpan = cSpan; 242 } else { 243 if (cSpan < (int)columns[m_cCol].span) 244 table()->splitColumn(m_cCol, cSpan); 245 currentSpan = columns[m_cCol].span; 246 } 247 for (int r = 0; r < rSpan; r++) { 248 CellStruct& c = cellAt(m_cRow + r, m_cCol); 249 ASSERT(cell); 250 c.cells.append(cell); 251 // If cells overlap then we take the slow path for painting. 252 if (c.cells.size() > 1) 253 m_hasMultipleCellLevels = true; 254 if (inColSpan) 255 c.inColSpan = true; 256 } 257 m_cCol++; 258 cSpan -= currentSpan; 259 inColSpan = true; 260 } 261 cell->setRow(m_cRow); 262 cell->setCol(table()->effColToCol(col)); 263 } 264 265 void RenderTableSection::setCellLogicalWidths() 266 { 267 Vector<int>& columnPos = table()->columnPositions(); 268 269 LayoutStateMaintainer statePusher(view()); 270 271 #ifdef ANDROID_LAYOUT 272 int visibleWidth = 0; 273 if (view()->frameView()) { 274 const Settings* settings = document()->settings(); 275 ASSERT(settings); 276 if (settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) 277 visibleWidth = view()->frameView()->textWrapWidth(); 278 } 279 #endif 280 281 for (int i = 0; i < m_gridRows; i++) { 282 Row& row = *m_grid[i].row; 283 int cols = row.size(); 284 for (int j = 0; j < cols; j++) { 285 CellStruct& current = row[j]; 286 RenderTableCell* cell = current.primaryCell(); 287 if (!cell || current.inColSpan) 288 continue; 289 int endCol = j; 290 int cspan = cell->colSpan(); 291 while (cspan && endCol < cols) { 292 ASSERT(endCol < (int)table()->columns().size()); 293 cspan -= table()->columns()[endCol].span; 294 endCol++; 295 } 296 int w = columnPos[endCol] - columnPos[j] - table()->hBorderSpacing(); 297 #ifdef ANDROID_LAYOUT 298 if (table()->isSingleColumn()) { 299 int b = table()->collapseBorders() ? 300 0 : table()->paddingLeft() + table()->paddingRight() + 2 * table()->hBorderSpacing(); 301 w = table()->width() - (table()->borderLeft() + table()->borderRight() + b); 302 } 303 #endif 304 int oldLogicalWidth = cell->logicalWidth(); 305 #ifdef ANDROID_LAYOUT 306 if (w != oldLogicalWidth || (visibleWidth > 0 && visibleWidth != cell->getVisibleWidth())) { 307 #else 308 if (w != oldLogicalWidth) { 309 #endif 310 cell->setNeedsLayout(true); 311 if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout()) { 312 if (!statePusher.didPush()) { 313 // Technically, we should also push state for the row, but since 314 // rows don't push a coordinate transform, that's not necessary. 315 statePusher.push(this, IntSize(x(), y())); 316 } 317 cell->repaint(); 318 } 319 #ifdef ANDROID_LAYOUT 320 if (w != oldLogicalWidth) 321 #endif 322 cell->updateLogicalWidth(w); 323 } 324 } 325 } 326 327 statePusher.pop(); // only pops if we pushed 328 } 329 330 int RenderTableSection::calcRowLogicalHeight() 331 { 332 #ifndef NDEBUG 333 setNeedsLayoutIsForbidden(true); 334 #endif 335 336 ASSERT(!needsLayout()); 337 #ifdef ANDROID_LAYOUT 338 if (table()->isSingleColumn()) { 339 int height = 0; 340 int spacing = table()->vBorderSpacing(); 341 for (int r = 0; r < m_gridRows; r++) 342 height += m_grid[r].logicalHeight.calcMinValue(0) + (m_grid[r].rowRenderer ? spacing : 0); 343 return height; 344 } 345 #endif 346 347 RenderTableCell* cell; 348 349 int spacing = table()->vBorderSpacing(); 350 351 LayoutStateMaintainer statePusher(view()); 352 353 m_rowPos.resize(m_gridRows + 1); 354 m_rowPos[0] = spacing; 355 356 for (int r = 0; r < m_gridRows; r++) { 357 m_rowPos[r + 1] = 0; 358 m_grid[r].baseline = 0; 359 int baseline = 0; 360 int bdesc = 0; 361 int ch = m_grid[r].logicalHeight.calcMinValue(0); 362 int pos = m_rowPos[r] + ch + (m_grid[r].rowRenderer ? spacing : 0); 363 364 m_rowPos[r + 1] = max(m_rowPos[r + 1], pos); 365 366 Row* row = m_grid[r].row; 367 int totalCols = row->size(); 368 369 for (int c = 0; c < totalCols; c++) { 370 CellStruct& current = cellAt(r, c); 371 cell = current.primaryCell(); 372 373 if (!cell || current.inColSpan) 374 continue; 375 376 if ((cell->row() + cell->rowSpan() - 1) > r) 377 continue; 378 379 int indx = max(r - cell->rowSpan() + 1, 0); 380 381 if (cell->overrideSize() != -1) { 382 if (!statePusher.didPush()) { 383 // Technically, we should also push state for the row, but since 384 // rows don't push a coordinate transform, that's not necessary. 385 statePusher.push(this, IntSize(x(), y())); 386 } 387 cell->setOverrideSize(-1); 388 cell->setChildNeedsLayout(true, false); 389 cell->layoutIfNeeded(); 390 } 391 392 int adjustedPaddingBefore = cell->paddingBefore() - cell->intrinsicPaddingBefore(); 393 int adjustedPaddingAfter = cell->paddingAfter() - cell->intrinsicPaddingAfter(); 394 int adjustedLogicalHeight = cell->logicalHeight() - (cell->intrinsicPaddingBefore() + cell->intrinsicPaddingAfter()); 395 396 // Explicit heights use the border box in quirks mode. In strict mode do the right 397 // thing and actually add in the border and padding. 398 ch = cell->style()->logicalHeight().calcValue(0) + 399 (document()->inQuirksMode() ? 0 : (adjustedPaddingBefore + adjustedPaddingAfter + 400 cell->borderBefore() + cell->borderAfter())); 401 ch = max(ch, adjustedLogicalHeight); 402 403 pos = m_rowPos[indx] + ch + (m_grid[r].rowRenderer ? spacing : 0); 404 405 m_rowPos[r + 1] = max(m_rowPos[r + 1], pos); 406 407 // find out the baseline 408 EVerticalAlign va = cell->style()->verticalAlign(); 409 if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB) { 410 int b = cell->cellBaselinePosition(); 411 if (b > cell->borderBefore() + cell->paddingBefore()) { 412 baseline = max(baseline, b - cell->intrinsicPaddingBefore()); 413 bdesc = max(bdesc, m_rowPos[indx] + ch - (b - cell->intrinsicPaddingBefore())); 414 } 415 } 416 } 417 418 // do we have baseline aligned elements? 419 if (baseline) { 420 // increase rowheight if baseline requires 421 m_rowPos[r + 1] = max(m_rowPos[r + 1], baseline + bdesc + (m_grid[r].rowRenderer ? spacing : 0)); 422 m_grid[r].baseline = baseline; 423 } 424 425 m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r]); 426 } 427 428 #ifndef NDEBUG 429 setNeedsLayoutIsForbidden(false); 430 #endif 431 432 ASSERT(!needsLayout()); 433 434 statePusher.pop(); 435 436 return m_rowPos[m_gridRows]; 437 } 438 439 void RenderTableSection::layout() 440 { 441 ASSERT(needsLayout()); 442 443 LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), style()->isFlippedBlocksWritingMode()); 444 for (RenderObject* child = children()->firstChild(); child; child = child->nextSibling()) { 445 if (child->isTableRow()) { 446 child->layoutIfNeeded(); 447 ASSERT(!child->needsLayout()); 448 } 449 } 450 statePusher.pop(); 451 setNeedsLayout(false); 452 } 453 454 int RenderTableSection::layoutRows(int toAdd) 455 { 456 #ifndef NDEBUG 457 setNeedsLayoutIsForbidden(true); 458 #endif 459 460 ASSERT(!needsLayout()); 461 #ifdef ANDROID_LAYOUT 462 if (table()->isSingleColumn()) { 463 int totalRows = m_gridRows; 464 int hspacing = table()->hBorderSpacing(); 465 int vspacing = table()->vBorderSpacing(); 466 int rHeight = vspacing; 467 468 int leftOffset = hspacing; 469 470 int nEffCols = table()->numEffCols(); 471 for (int r = 0; r < totalRows; r++) { 472 for (int c = 0; c < nEffCols; c++) { 473 CellStruct current = cellAt(r, c); 474 RenderTableCell* cell = current.primaryCell(); 475 476 if (!cell || current.inColSpan) 477 continue; 478 if (r > 0 && (primaryCellAt(r-1, c) == cell)) 479 continue; 480 481 // cell->setCellTopExtra(0); 482 // cell->setCellBottomExtra(0); 483 484 int oldCellX = cell->x(); 485 int oldCellY = cell->y(); 486 487 if (style()->direction() == RTL) { 488 cell->setX(table()->width()); 489 cell->setY(rHeight); 490 } else { 491 cell->setX(leftOffset); 492 cell->setY(rHeight); 493 } 494 495 // If the cell moved, we have to repaint it as well as any floating/positioned 496 // descendants. An exception is if we need a layout. In this case, we know we're going to 497 // repaint ourselves (and the cell) anyway. 498 if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout()) { 499 // IntRect cellRect(oldCellX, oldCellY - cell->borderTopExtra() , cell->width(), cell->height()); 500 IntRect cellRect(oldCellX, oldCellY, cell->width(), cell->height()); 501 cell->repaintDuringLayoutIfMoved(cellRect); 502 } 503 rHeight += cell->height() + vspacing; 504 } 505 } 506 507 setHeight(rHeight); 508 return height(); 509 } 510 #endif 511 512 int rHeight; 513 int rindx; 514 int totalRows = m_gridRows; 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_hasOverflowingCell = false; 520 521 if (toAdd && totalRows && (m_rowPos[totalRows] || !nextSibling())) { 522 int totalHeight = m_rowPos[totalRows] + toAdd; 523 524 int dh = toAdd; 525 int totalPercent = 0; 526 int numAuto = 0; 527 for (int r = 0; r < totalRows; r++) { 528 if (m_grid[r].logicalHeight.isAuto()) 529 numAuto++; 530 else if (m_grid[r].logicalHeight.isPercent()) 531 totalPercent += m_grid[r].logicalHeight.percent(); 532 } 533 if (totalPercent) { 534 // try to satisfy percent 535 int add = 0; 536 totalPercent = min(totalPercent, 100); 537 int rh = m_rowPos[1] - m_rowPos[0]; 538 for (int r = 0; r < totalRows; r++) { 539 if (totalPercent > 0 && m_grid[r].logicalHeight.isPercent()) { 540 int toAdd = min(dh, static_cast<int>((totalHeight * m_grid[r].logicalHeight.percent() / 100) - rh)); 541 // If toAdd is negative, then we don't want to shrink the row (this bug 542 // affected Outlook Web Access). 543 toAdd = max(0, toAdd); 544 add += toAdd; 545 dh -= toAdd; 546 totalPercent -= m_grid[r].logicalHeight.percent(); 547 } 548 if (r < totalRows - 1) 549 rh = m_rowPos[r + 2] - m_rowPos[r + 1]; 550 m_rowPos[r + 1] += add; 551 } 552 } 553 if (numAuto) { 554 // distribute over variable cols 555 int add = 0; 556 for (int r = 0; r < totalRows; r++) { 557 if (numAuto > 0 && m_grid[r].logicalHeight.isAuto()) { 558 int toAdd = dh / numAuto; 559 add += toAdd; 560 dh -= toAdd; 561 numAuto--; 562 } 563 m_rowPos[r + 1] += add; 564 } 565 } 566 if (dh > 0 && m_rowPos[totalRows]) { 567 // if some left overs, distribute equally. 568 int tot = m_rowPos[totalRows]; 569 int add = 0; 570 int prev = m_rowPos[0]; 571 for (int r = 0; r < totalRows; r++) { 572 // weight with the original height 573 add += dh * (m_rowPos[r + 1] - prev) / tot; 574 prev = m_rowPos[r + 1]; 575 m_rowPos[r + 1] += add; 576 } 577 } 578 } 579 580 int hspacing = table()->hBorderSpacing(); 581 int vspacing = table()->vBorderSpacing(); 582 int nEffCols = table()->numEffCols(); 583 584 LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), style()->isFlippedBlocksWritingMode()); 585 586 for (int r = 0; r < totalRows; r++) { 587 // Set the row's x/y position and width/height. 588 if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) { 589 rowRenderer->setLocation(0, m_rowPos[r]); 590 rowRenderer->setLogicalWidth(logicalWidth()); 591 rowRenderer->setLogicalHeight(m_rowPos[r + 1] - m_rowPos[r] - vspacing); 592 rowRenderer->updateLayerTransform(); 593 } 594 595 for (int c = 0; c < nEffCols; c++) { 596 CellStruct& cs = cellAt(r, c); 597 RenderTableCell* cell = cs.primaryCell(); 598 599 if (!cell || cs.inColSpan) 600 continue; 601 602 rindx = cell->row(); 603 rHeight = m_rowPos[rindx + cell->rowSpan()] - m_rowPos[rindx] - vspacing; 604 605 // Force percent height children to lay themselves out again. 606 // This will cause these children to grow to fill the cell. 607 // FIXME: There is still more work to do here to fully match WinIE (should 608 // it become necessary to do so). In quirks mode, WinIE behaves like we 609 // do, but it will clip the cells that spill out of the table section. In 610 // strict mode, Mozilla and WinIE both regrow the table to accommodate the 611 // new height of the cell (thus letting the percentages cause growth one 612 // time only). We may also not be handling row-spanning cells correctly. 613 // 614 // Note also the oddity where replaced elements always flex, and yet blocks/tables do 615 // not necessarily flex. WinIE is crazy and inconsistent, and we can't hope to 616 // match the behavior perfectly, but we'll continue to refine it as we discover new 617 // bugs. :) 618 bool cellChildrenFlex = false; 619 bool flexAllChildren = cell->style()->logicalHeight().isFixed() 620 || (!table()->style()->logicalHeight().isAuto() && rHeight != cell->logicalHeight()); 621 622 for (RenderObject* o = cell->firstChild(); o; o = o->nextSibling()) { 623 if (!o->isText() && o->style()->logicalHeight().isPercent() && (flexAllChildren || o->isReplaced() || (o->isBox() && toRenderBox(o)->scrollsOverflow()))) { 624 // Tables with no sections do not flex. 625 if (!o->isTable() || toRenderTable(o)->hasSections()) { 626 o->setNeedsLayout(true, false); 627 cellChildrenFlex = true; 628 } 629 } 630 } 631 632 if (HashSet<RenderBox*>* percentHeightDescendants = cell->percentHeightDescendants()) { 633 HashSet<RenderBox*>::iterator end = percentHeightDescendants->end(); 634 for (HashSet<RenderBox*>::iterator it = percentHeightDescendants->begin(); it != end; ++it) { 635 RenderBox* box = *it; 636 if (!box->isReplaced() && !box->scrollsOverflow() && !flexAllChildren) 637 continue; 638 639 while (box != cell) { 640 if (box->normalChildNeedsLayout()) 641 break; 642 box->setChildNeedsLayout(true, false); 643 box = box->containingBlock(); 644 ASSERT(box); 645 if (!box) 646 break; 647 } 648 cellChildrenFlex = true; 649 } 650 } 651 652 if (cellChildrenFlex) { 653 cell->setChildNeedsLayout(true, false); 654 // Alignment within a cell is based off the calculated 655 // height, which becomes irrelevant once the cell has 656 // been resized based off its percentage. 657 cell->setOverrideSizeFromRowHeight(rHeight); 658 cell->layoutIfNeeded(); 659 660 // If the baseline moved, we may have to update the data for our row. Find out the new baseline. 661 EVerticalAlign va = cell->style()->verticalAlign(); 662 if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB) { 663 int b = cell->cellBaselinePosition(); 664 if (b > cell->borderBefore() + cell->paddingBefore()) 665 m_grid[r].baseline = max(m_grid[r].baseline, b); 666 } 667 } 668 669 int oldIntrinsicPaddingBefore = cell->intrinsicPaddingBefore(); 670 int oldIntrinsicPaddingAfter = cell->intrinsicPaddingAfter(); 671 int logicalHeightWithoutIntrinsicPadding = cell->logicalHeight() - oldIntrinsicPaddingBefore - oldIntrinsicPaddingAfter; 672 673 int intrinsicPaddingBefore = 0; 674 switch (cell->style()->verticalAlign()) { 675 case SUB: 676 case SUPER: 677 case TEXT_TOP: 678 case TEXT_BOTTOM: 679 case BASELINE: { 680 int b = cell->cellBaselinePosition(); 681 if (b > cell->borderBefore() + cell->paddingBefore()) 682 intrinsicPaddingBefore = getBaseline(r) - (b - oldIntrinsicPaddingBefore); 683 break; 684 } 685 case TOP: 686 break; 687 case MIDDLE: 688 intrinsicPaddingBefore = (rHeight - logicalHeightWithoutIntrinsicPadding) / 2; 689 break; 690 case BOTTOM: 691 intrinsicPaddingBefore = rHeight - logicalHeightWithoutIntrinsicPadding; 692 break; 693 default: 694 break; 695 } 696 697 int intrinsicPaddingAfter = rHeight - logicalHeightWithoutIntrinsicPadding - intrinsicPaddingBefore; 698 cell->setIntrinsicPaddingBefore(intrinsicPaddingBefore); 699 cell->setIntrinsicPaddingAfter(intrinsicPaddingAfter); 700 701 IntRect oldCellRect(cell->x(), cell->y() , cell->width(), cell->height()); 702 703 if (!style()->isLeftToRightDirection()) 704 cell->setLogicalLocation(table()->columnPositions()[nEffCols] - table()->columnPositions()[table()->colToEffCol(cell->col() + cell->colSpan())] + hspacing, m_rowPos[rindx]); 705 else 706 cell->setLogicalLocation(table()->columnPositions()[c] + hspacing, m_rowPos[rindx]); 707 view()->addLayoutDelta(IntSize(oldCellRect.x() - cell->x(), oldCellRect.y() - cell->y())); 708 709 if (intrinsicPaddingBefore != oldIntrinsicPaddingBefore || intrinsicPaddingAfter != oldIntrinsicPaddingAfter) 710 cell->setNeedsLayout(true, false); 711 712 if (!cell->needsLayout() && view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(cell->logicalTop()) != cell->pageLogicalOffset()) 713 cell->setChildNeedsLayout(true, false); 714 715 cell->layoutIfNeeded(); 716 717 // FIXME: Make pagination work with vertical tables. 718 if (style()->isHorizontalWritingMode() && view()->layoutState()->pageLogicalHeight() && cell->height() != rHeight) 719 cell->setHeight(rHeight); // FIXME: Pagination might have made us change size. For now just shrink or grow the cell to fit without doing a relayout. 720 721 IntSize childOffset(cell->x() - oldCellRect.x(), cell->y() - oldCellRect.y()); 722 if (childOffset.width() || childOffset.height()) { 723 view()->addLayoutDelta(childOffset); 724 725 // If the child moved, we have to repaint it as well as any floating/positioned 726 // descendants. An exception is if we need a layout. In this case, we know we're going to 727 // repaint ourselves (and the child) anyway. 728 if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout()) 729 cell->repaintDuringLayoutIfMoved(oldCellRect); 730 } 731 } 732 } 733 734 #ifndef NDEBUG 735 setNeedsLayoutIsForbidden(false); 736 #endif 737 738 ASSERT(!needsLayout()); 739 740 setLogicalHeight(m_rowPos[totalRows]); 741 742 // Now that our height has been determined, add in overflow from cells. 743 for (int r = 0; r < totalRows; r++) { 744 for (int c = 0; c < nEffCols; c++) { 745 CellStruct& cs = cellAt(r, c); 746 RenderTableCell* cell = cs.primaryCell(); 747 if (!cell || cs.inColSpan) 748 continue; 749 if (r < totalRows - 1 && cell == primaryCellAt(r + 1, c)) 750 continue; 751 addOverflowFromChild(cell); 752 m_hasOverflowingCell |= cell->hasVisualOverflow(); 753 } 754 } 755 756 statePusher.pop(); 757 return height(); 758 } 759 760 int RenderTableSection::calcOuterBorderBefore() const 761 { 762 int totalCols = table()->numEffCols(); 763 if (!m_gridRows || !totalCols) 764 return 0; 765 766 unsigned borderWidth = 0; 767 768 const BorderValue& sb = style()->borderBefore(); 769 if (sb.style() == BHIDDEN) 770 return -1; 771 if (sb.style() > BHIDDEN) 772 borderWidth = sb.width(); 773 774 const BorderValue& rb = firstChild()->style()->borderBefore(); 775 if (rb.style() == BHIDDEN) 776 return -1; 777 if (rb.style() > BHIDDEN && rb.width() > borderWidth) 778 borderWidth = rb.width(); 779 780 bool allHidden = true; 781 for (int c = 0; c < totalCols; c++) { 782 const CellStruct& current = cellAt(0, c); 783 if (current.inColSpan || !current.hasCells()) 784 continue; 785 const BorderValue& cb = current.primaryCell()->style()->borderBefore(); // FIXME: Make this work with perpendicular and flipped cells. 786 // FIXME: Don't repeat for the same col group 787 RenderTableCol* colGroup = table()->colElement(c); 788 if (colGroup) { 789 const BorderValue& gb = colGroup->style()->borderBefore(); 790 if (gb.style() == BHIDDEN || cb.style() == BHIDDEN) 791 continue; 792 allHidden = false; 793 if (gb.style() > BHIDDEN && gb.width() > borderWidth) 794 borderWidth = gb.width(); 795 if (cb.style() > BHIDDEN && cb.width() > borderWidth) 796 borderWidth = cb.width(); 797 } else { 798 if (cb.style() == BHIDDEN) 799 continue; 800 allHidden = false; 801 if (cb.style() > BHIDDEN && cb.width() > borderWidth) 802 borderWidth = cb.width(); 803 } 804 } 805 if (allHidden) 806 return -1; 807 808 return borderWidth / 2; 809 } 810 811 int RenderTableSection::calcOuterBorderAfter() const 812 { 813 int totalCols = table()->numEffCols(); 814 if (!m_gridRows || !totalCols) 815 return 0; 816 817 unsigned borderWidth = 0; 818 819 const BorderValue& sb = style()->borderAfter(); 820 if (sb.style() == BHIDDEN) 821 return -1; 822 if (sb.style() > BHIDDEN) 823 borderWidth = sb.width(); 824 825 const BorderValue& rb = lastChild()->style()->borderAfter(); 826 if (rb.style() == BHIDDEN) 827 return -1; 828 if (rb.style() > BHIDDEN && rb.width() > borderWidth) 829 borderWidth = rb.width(); 830 831 bool allHidden = true; 832 for (int c = 0; c < totalCols; c++) { 833 const CellStruct& current = cellAt(m_gridRows - 1, c); 834 if (current.inColSpan || !current.hasCells()) 835 continue; 836 const BorderValue& cb = current.primaryCell()->style()->borderAfter(); // FIXME: Make this work with perpendicular and flipped cells. 837 // FIXME: Don't repeat for the same col group 838 RenderTableCol* colGroup = table()->colElement(c); 839 if (colGroup) { 840 const BorderValue& gb = colGroup->style()->borderAfter(); 841 if (gb.style() == BHIDDEN || cb.style() == BHIDDEN) 842 continue; 843 allHidden = false; 844 if (gb.style() > BHIDDEN && gb.width() > borderWidth) 845 borderWidth = gb.width(); 846 if (cb.style() > BHIDDEN && cb.width() > borderWidth) 847 borderWidth = cb.width(); 848 } else { 849 if (cb.style() == BHIDDEN) 850 continue; 851 allHidden = false; 852 if (cb.style() > BHIDDEN && cb.width() > borderWidth) 853 borderWidth = cb.width(); 854 } 855 } 856 if (allHidden) 857 return -1; 858 859 return (borderWidth + 1) / 2; 860 } 861 862 int RenderTableSection::calcOuterBorderStart() const 863 { 864 int totalCols = table()->numEffCols(); 865 if (!m_gridRows || !totalCols) 866 return 0; 867 868 unsigned borderWidth = 0; 869 870 const BorderValue& sb = style()->borderStart(); 871 if (sb.style() == BHIDDEN) 872 return -1; 873 if (sb.style() > BHIDDEN) 874 borderWidth = sb.width(); 875 876 if (RenderTableCol* colGroup = table()->colElement(0)) { 877 const BorderValue& gb = colGroup->style()->borderStart(); 878 if (gb.style() == BHIDDEN) 879 return -1; 880 if (gb.style() > BHIDDEN && gb.width() > borderWidth) 881 borderWidth = gb.width(); 882 } 883 884 bool allHidden = true; 885 for (int r = 0; r < m_gridRows; r++) { 886 const CellStruct& current = cellAt(r, 0); 887 if (!current.hasCells()) 888 continue; 889 // FIXME: Don't repeat for the same cell 890 const BorderValue& cb = current.primaryCell()->style()->borderStart(); // FIXME: Make this work with perpendicular and flipped cells. 891 const BorderValue& rb = current.primaryCell()->parent()->style()->borderStart(); 892 if (cb.style() == BHIDDEN || rb.style() == BHIDDEN) 893 continue; 894 allHidden = false; 895 if (cb.style() > BHIDDEN && cb.width() > borderWidth) 896 borderWidth = cb.width(); 897 if (rb.style() > BHIDDEN && rb.width() > borderWidth) 898 borderWidth = rb.width(); 899 } 900 if (allHidden) 901 return -1; 902 903 return (borderWidth + (table()->style()->isLeftToRightDirection() ? 0 : 1)) / 2; 904 } 905 906 int RenderTableSection::calcOuterBorderEnd() const 907 { 908 int totalCols = table()->numEffCols(); 909 if (!m_gridRows || !totalCols) 910 return 0; 911 912 unsigned borderWidth = 0; 913 914 const BorderValue& sb = style()->borderEnd(); 915 if (sb.style() == BHIDDEN) 916 return -1; 917 if (sb.style() > BHIDDEN) 918 borderWidth = sb.width(); 919 920 if (RenderTableCol* colGroup = table()->colElement(totalCols - 1)) { 921 const BorderValue& gb = colGroup->style()->borderEnd(); 922 if (gb.style() == BHIDDEN) 923 return -1; 924 if (gb.style() > BHIDDEN && gb.width() > borderWidth) 925 borderWidth = gb.width(); 926 } 927 928 bool allHidden = true; 929 for (int r = 0; r < m_gridRows; r++) { 930 const CellStruct& current = cellAt(r, totalCols - 1); 931 if (!current.hasCells()) 932 continue; 933 // FIXME: Don't repeat for the same cell 934 const BorderValue& cb = current.primaryCell()->style()->borderEnd(); // FIXME: Make this work with perpendicular and flipped cells. 935 const BorderValue& rb = current.primaryCell()->parent()->style()->borderEnd(); 936 if (cb.style() == BHIDDEN || rb.style() == BHIDDEN) 937 continue; 938 allHidden = false; 939 if (cb.style() > BHIDDEN && cb.width() > borderWidth) 940 borderWidth = cb.width(); 941 if (rb.style() > BHIDDEN && rb.width() > borderWidth) 942 borderWidth = rb.width(); 943 } 944 if (allHidden) 945 return -1; 946 947 return (borderWidth + (table()->style()->isLeftToRightDirection() ? 1 : 0)) / 2; 948 } 949 950 void RenderTableSection::recalcOuterBorder() 951 { 952 m_outerBorderBefore = calcOuterBorderBefore(); 953 m_outerBorderAfter = calcOuterBorderAfter(); 954 m_outerBorderStart = calcOuterBorderStart(); 955 m_outerBorderEnd = calcOuterBorderEnd(); 956 } 957 958 int RenderTableSection::firstLineBoxBaseline() const 959 { 960 if (!m_gridRows) 961 return -1; 962 963 int firstLineBaseline = m_grid[0].baseline; 964 if (firstLineBaseline) 965 return firstLineBaseline + m_rowPos[0]; 966 967 firstLineBaseline = -1; 968 Row* firstRow = m_grid[0].row; 969 for (size_t i = 0; i < firstRow->size(); ++i) { 970 CellStruct& cs = firstRow->at(i); 971 RenderTableCell* cell = cs.primaryCell(); 972 if (cell) 973 firstLineBaseline = max(firstLineBaseline, cell->logicalTop() + cell->paddingBefore() + cell->borderBefore() + cell->contentLogicalHeight()); 974 } 975 976 return firstLineBaseline; 977 } 978 979 void RenderTableSection::paint(PaintInfo& paintInfo, int tx, int ty) 980 { 981 // put this back in when all layout tests can handle it 982 // ASSERT(!needsLayout()); 983 // avoid crashing on bugs that cause us to paint with dirty layout 984 if (needsLayout()) 985 return; 986 987 unsigned totalRows = m_gridRows; 988 unsigned totalCols = table()->columns().size(); 989 990 if (!totalRows || !totalCols) 991 return; 992 993 tx += x(); 994 ty += y(); 995 996 PaintPhase phase = paintInfo.phase; 997 bool pushedClip = pushContentsClip(paintInfo, tx, ty); 998 paintObject(paintInfo, tx, ty); 999 if (pushedClip) 1000 popContentsClip(paintInfo, phase, tx, ty); 1001 } 1002 1003 static inline bool compareCellPositions(RenderTableCell* elem1, RenderTableCell* elem2) 1004 { 1005 return elem1->row() < elem2->row(); 1006 } 1007 1008 void RenderTableSection::paintCell(RenderTableCell* cell, PaintInfo& paintInfo, int tx, int ty) 1009 { 1010 IntPoint cellPoint = flipForWritingMode(cell, IntPoint(tx, ty), ParentToChildFlippingAdjustment); 1011 PaintPhase paintPhase = paintInfo.phase; 1012 RenderTableRow* row = toRenderTableRow(cell->parent()); 1013 1014 if (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) { 1015 // We need to handle painting a stack of backgrounds. This stack (from bottom to top) consists of 1016 // the column group, column, row group, row, and then the cell. 1017 RenderObject* col = table()->colElement(cell->col()); 1018 RenderObject* colGroup = 0; 1019 if (col && col->parent()->style()->display() == TABLE_COLUMN_GROUP) 1020 colGroup = col->parent(); 1021 1022 // Column groups and columns first. 1023 // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in 1024 // the stack, since we have already opened a transparency layer (potentially) for the table row group. 1025 // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the 1026 // cell. 1027 cell->paintBackgroundsBehindCell(paintInfo, cellPoint.x(), cellPoint.y(), colGroup); 1028 cell->paintBackgroundsBehindCell(paintInfo, cellPoint.x(), cellPoint.y(), col); 1029 1030 // Paint the row group next. 1031 cell->paintBackgroundsBehindCell(paintInfo, cellPoint.x(), cellPoint.y(), this); 1032 1033 // Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for 1034 // painting the row background for the cell. 1035 if (!row->hasSelfPaintingLayer()) 1036 cell->paintBackgroundsBehindCell(paintInfo, cellPoint.x(), cellPoint.y(), row); 1037 } 1038 if ((!cell->hasSelfPaintingLayer() && !row->hasSelfPaintingLayer()) || paintInfo.phase == PaintPhaseCollapsedTableBorders) 1039 cell->paint(paintInfo, cellPoint.x(), cellPoint.y()); 1040 } 1041 1042 void RenderTableSection::paintObject(PaintInfo& paintInfo, int tx, int ty) 1043 { 1044 // Check which rows and cols are visible and only paint these. 1045 // FIXME: Could use a binary search here. 1046 unsigned totalRows = m_gridRows; 1047 unsigned totalCols = table()->columns().size(); 1048 1049 PaintPhase paintPhase = paintInfo.phase; 1050 1051 #ifdef ANDROID_LAYOUT 1052 unsigned int startrow = 0; 1053 unsigned int endrow = totalRows; 1054 unsigned int startcol = 0; 1055 unsigned int endcol = totalCols; 1056 if (table()->isSingleColumn()) { 1057 // FIXME: should we be smarter too? 1058 } else { 1059 // FIXME: possible to rollback to the common tree. 1060 // rowPos size is set in calcRowHeight(), which is called from table layout(). 1061 // BUT RenderTableSection is init through parsing. On a slow device, paint() as 1062 // the result of layout() can come after the next parse() as everything is triggered 1063 // by timer. So we have to check rowPos before using it. 1064 if (m_rowPos.size() != (totalRows + 1)) 1065 return; 1066 #endif 1067 1068 int os = 2 * maximalOutlineSize(paintPhase); 1069 unsigned startrow = 0; 1070 unsigned endrow = totalRows; 1071 1072 IntRect localRepaintRect = paintInfo.rect; 1073 localRepaintRect.move(-tx, -ty); 1074 if (style()->isFlippedBlocksWritingMode()) { 1075 if (style()->isHorizontalWritingMode()) 1076 localRepaintRect.setY(height() - localRepaintRect.maxY()); 1077 else 1078 localRepaintRect.setX(width() - localRepaintRect.maxX()); 1079 } 1080 1081 // If some cell overflows, just paint all of them. 1082 if (!m_hasOverflowingCell) { 1083 int before = (style()->isHorizontalWritingMode() ? localRepaintRect.y() : localRepaintRect.x()) - os; 1084 // binary search to find a row 1085 startrow = std::lower_bound(m_rowPos.begin(), m_rowPos.end(), before) - m_rowPos.begin(); 1086 1087 // The binary search above gives us the first row with 1088 // a y position >= the top of the paint rect. Thus, the previous 1089 // may need to be repainted as well. 1090 if (startrow == m_rowPos.size() || (startrow > 0 && (m_rowPos[startrow] > before))) 1091 --startrow; 1092 1093 int after = (style()->isHorizontalWritingMode() ? localRepaintRect.maxY() : localRepaintRect.maxX()) + os; 1094 endrow = std::lower_bound(m_rowPos.begin(), m_rowPos.end(), after) - m_rowPos.begin(); 1095 if (endrow == m_rowPos.size()) 1096 --endrow; 1097 1098 if (!endrow && m_rowPos[0] - table()->outerBorderBefore() <= after) 1099 ++endrow; 1100 } 1101 1102 unsigned startcol = 0; 1103 unsigned endcol = totalCols; 1104 // FIXME: Implement RTL. 1105 if (!m_hasOverflowingCell && style()->isLeftToRightDirection()) { 1106 int start = (style()->isHorizontalWritingMode() ? localRepaintRect.x() : localRepaintRect.y()) - os; 1107 Vector<int>& columnPos = table()->columnPositions(); 1108 startcol = std::lower_bound(columnPos.begin(), columnPos.end(), start) - columnPos.begin(); 1109 if ((startcol == columnPos.size()) || (startcol > 0 && (columnPos[startcol] > start))) 1110 --startcol; 1111 1112 int end = (style()->isHorizontalWritingMode() ? localRepaintRect.maxX() : localRepaintRect.maxY()) + os; 1113 endcol = std::lower_bound(columnPos.begin(), columnPos.end(), end) - columnPos.begin(); 1114 if (endcol == columnPos.size()) 1115 --endcol; 1116 1117 if (!endcol && columnPos[0] - table()->outerBorderStart() <= end) 1118 ++endcol; 1119 } 1120 1121 #ifdef ANDROID_LAYOUT 1122 } 1123 #endif 1124 1125 if (startcol < endcol) { 1126 if (!m_hasMultipleCellLevels) { 1127 // Draw the dirty cells in the order that they appear. 1128 for (unsigned r = startrow; r < endrow; r++) { 1129 for (unsigned c = startcol; c < endcol; c++) { 1130 CellStruct& current = cellAt(r, c); 1131 RenderTableCell* cell = current.primaryCell(); 1132 if (!cell || (r > startrow && primaryCellAt(r - 1, c) == cell) || (c > startcol && primaryCellAt(r, c - 1) == cell)) 1133 continue; 1134 paintCell(cell, paintInfo, tx, ty); 1135 } 1136 } 1137 } else { 1138 // Draw the cells in the correct paint order. 1139 Vector<RenderTableCell*> cells; 1140 HashSet<RenderTableCell*> spanningCells; 1141 for (unsigned r = startrow; r < endrow; r++) { 1142 for (unsigned c = startcol; c < endcol; c++) { 1143 CellStruct& current = cellAt(r, c); 1144 if (!current.hasCells()) 1145 continue; 1146 for (unsigned i = 0; i < current.cells.size(); ++i) { 1147 if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) { 1148 if (spanningCells.contains(current.cells[i])) 1149 continue; 1150 spanningCells.add(current.cells[i]); 1151 } 1152 cells.append(current.cells[i]); 1153 } 1154 } 1155 } 1156 // Sort the dirty cells by paint order. 1157 std::stable_sort(cells.begin(), cells.end(), compareCellPositions); 1158 int size = cells.size(); 1159 // Paint the cells. 1160 for (int i = 0; i < size; ++i) 1161 paintCell(cells[i], paintInfo, tx, ty); 1162 } 1163 } 1164 } 1165 1166 void RenderTableSection::imageChanged(WrappedImagePtr, const IntRect*) 1167 { 1168 // FIXME: Examine cells and repaint only the rect the image paints in. 1169 repaint(); 1170 } 1171 1172 void RenderTableSection::recalcCells() 1173 { 1174 m_cCol = 0; 1175 m_cRow = -1; 1176 clearGrid(); 1177 m_gridRows = 0; 1178 1179 for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { 1180 if (row->isTableRow()) { 1181 m_cRow++; 1182 m_cCol = 0; 1183 if (!ensureRows(m_cRow + 1)) 1184 break; 1185 1186 RenderTableRow* tableRow = toRenderTableRow(row); 1187 m_grid[m_cRow].rowRenderer = tableRow; 1188 setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(&m_grid[m_cRow]); 1189 1190 for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { 1191 if (cell->isTableCell()) 1192 addCell(toRenderTableCell(cell), tableRow); 1193 } 1194 } 1195 } 1196 m_needsCellRecalc = false; 1197 setNeedsLayout(true); 1198 } 1199 1200 void RenderTableSection::setNeedsCellRecalc() 1201 { 1202 m_needsCellRecalc = true; 1203 if (RenderTable* t = table()) 1204 t->setNeedsSectionRecalc(); 1205 } 1206 1207 void RenderTableSection::clearGrid() 1208 { 1209 int rows = m_gridRows; 1210 while (rows--) 1211 delete m_grid[rows].row; 1212 } 1213 1214 int RenderTableSection::numColumns() const 1215 { 1216 int result = 0; 1217 1218 for (int r = 0; r < m_gridRows; ++r) { 1219 for (int c = result; c < table()->numEffCols(); ++c) { 1220 const CellStruct& cell = cellAt(r, c); 1221 if (cell.hasCells() || cell.inColSpan) 1222 result = c; 1223 } 1224 } 1225 1226 return result + 1; 1227 } 1228 1229 void RenderTableSection::appendColumn(int pos) 1230 { 1231 for (int row = 0; row < m_gridRows; ++row) 1232 m_grid[row].row->resize(pos + 1); 1233 } 1234 1235 void RenderTableSection::splitColumn(int pos, int first) 1236 { 1237 if (m_cCol > pos) 1238 m_cCol++; 1239 for (int row = 0; row < m_gridRows; ++row) { 1240 Row& r = *m_grid[row].row; 1241 r.insert(pos + 1, CellStruct()); 1242 if (r[pos].hasCells()) { 1243 r[pos + 1].cells.append(r[pos].cells); 1244 RenderTableCell* cell = r[pos].primaryCell(); 1245 ASSERT(cell); 1246 int colleft = cell->colSpan() - r[pos].inColSpan; 1247 if (first > colleft) 1248 r[pos + 1].inColSpan = 0; 1249 else 1250 r[pos + 1].inColSpan = first + r[pos].inColSpan; 1251 } else { 1252 r[pos + 1].inColSpan = 0; 1253 } 1254 } 1255 } 1256 1257 // Hit Testing 1258 bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action) 1259 { 1260 // If we have no children then we have nothing to do. 1261 if (!firstChild()) 1262 return false; 1263 1264 // Table sections cannot ever be hit tested. Effectively they do not exist. 1265 // Just forward to our children always. 1266 tx += x(); 1267 ty += y(); 1268 1269 if (hasOverflowClip() && !overflowClipRect(tx, ty).intersects(result.rectForPoint(xPos, yPos))) 1270 return false; 1271 1272 if (m_hasOverflowingCell) { 1273 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { 1274 // FIXME: We have to skip over inline flows, since they can show up inside table rows 1275 // at the moment (a demoted inline <form> for example). If we ever implement a 1276 // table-specific hit-test method (which we should do for performance reasons anyway), 1277 // then we can remove this check. 1278 if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer()) { 1279 IntPoint childPoint = flipForWritingMode(toRenderBox(child), IntPoint(tx, ty), ParentToChildFlippingAdjustment); 1280 if (child->nodeAtPoint(request, result, xPos, yPos, childPoint.x(), childPoint.y(), action)) { 1281 updateHitTestResult(result, IntPoint(xPos - childPoint.x(), yPos - childPoint.y())); 1282 return true; 1283 } 1284 } 1285 } 1286 return false; 1287 } 1288 1289 IntPoint location = IntPoint(xPos - tx, yPos - ty); 1290 if (style()->isFlippedBlocksWritingMode()) { 1291 if (style()->isHorizontalWritingMode()) 1292 location.setY(height() - location.y()); 1293 else 1294 location.setX(width() - location.x()); 1295 } 1296 1297 int offsetInColumnDirection = style()->isHorizontalWritingMode() ? location.y() : location.x(); 1298 // Find the first row that starts after offsetInColumnDirection. 1299 unsigned nextRow = std::upper_bound(m_rowPos.begin(), m_rowPos.end(), offsetInColumnDirection) - m_rowPos.begin(); 1300 if (nextRow == m_rowPos.size()) 1301 return false; 1302 // Now set hitRow to the index of the hit row, or 0. 1303 unsigned hitRow = nextRow > 0 ? nextRow - 1 : 0; 1304 1305 Vector<int>& columnPos = table()->columnPositions(); 1306 int offsetInRowDirection = style()->isHorizontalWritingMode() ? location.x() : location.y(); 1307 if (!style()->isLeftToRightDirection()) 1308 offsetInRowDirection = columnPos[columnPos.size() - 1] - offsetInRowDirection; 1309 1310 unsigned nextColumn = std::lower_bound(columnPos.begin(), columnPos.end(), offsetInRowDirection) - columnPos.begin(); 1311 if (nextColumn == columnPos.size()) 1312 return false; 1313 unsigned hitColumn = nextColumn > 0 ? nextColumn - 1 : 0; 1314 1315 CellStruct& current = cellAt(hitRow, hitColumn); 1316 1317 // If the cell is empty, there's nothing to do 1318 if (!current.hasCells()) 1319 return false; 1320 1321 for (int i = current.cells.size() - 1; i >= 0; --i) { 1322 RenderTableCell* cell = current.cells[i]; 1323 IntPoint cellPoint = flipForWritingMode(cell, IntPoint(tx, ty), ParentToChildFlippingAdjustment); 1324 if (static_cast<RenderObject*>(cell)->nodeAtPoint(request, result, xPos, yPos, cellPoint.x(), cellPoint.y(), action)) { 1325 updateHitTestResult(result, IntPoint(xPos - cellPoint.x(), yPos - cellPoint.y())); 1326 return true; 1327 } 1328 } 1329 return false; 1330 1331 } 1332 1333 } // namespace WebCore 1334