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