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, 2007, 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 "RenderTable.h" 28 29#include "AutoTableLayout.h" 30#include "CollapsedBorderValue.h" 31#include "Document.h" 32#include "FixedTableLayout.h" 33#include "FrameView.h" 34#include "HitTestResult.h" 35#include "HTMLNames.h" 36#include "LayoutRepainter.h" 37#include "RenderLayer.h" 38#include "RenderTableCaption.h" 39#include "RenderTableCell.h" 40#include "RenderTableCol.h" 41#include "RenderTableSection.h" 42#include "RenderView.h" 43#include "StyleInheritedData.h" 44#include <wtf/StackStats.h> 45 46using namespace std; 47 48namespace WebCore { 49 50using namespace HTMLNames; 51 52RenderTable::RenderTable(Element* element) 53 : RenderBlock(element) 54 , m_head(0) 55 , m_foot(0) 56 , m_firstBody(0) 57 , m_currentBorder(0) 58 , m_collapsedBordersValid(false) 59 , m_hasColElements(false) 60 , m_needsSectionRecalc(false) 61 , m_columnLogicalWidthChanged(false) 62 , m_columnRenderersValid(false) 63 , m_hSpacing(0) 64 , m_vSpacing(0) 65 , m_borderStart(0) 66 , m_borderEnd(0) 67{ 68 setChildrenInline(false); 69 m_columnPos.fill(0, 1); 70 71} 72 73RenderTable::~RenderTable() 74{ 75} 76 77void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 78{ 79 RenderBlock::styleDidChange(diff, oldStyle); 80 propagateStyleToAnonymousChildren(); 81 82 ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO; 83 84 // In the collapsed border model, there is no cell spacing. 85 m_hSpacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing(); 86 m_vSpacing = collapseBorders() ? 0 : style()->verticalBorderSpacing(); 87 m_columnPos[0] = m_hSpacing; 88 89 if (!m_tableLayout || style()->tableLayout() != oldTableLayout) { 90 // According to the CSS2 spec, you only use fixed table layout if an 91 // explicit width is specified on the table. Auto width implies auto table layout. 92 if (style()->tableLayout() == TFIXED && !style()->logicalWidth().isAuto()) 93 m_tableLayout = adoptPtr(new FixedTableLayout(this)); 94 else 95 m_tableLayout = adoptPtr(new AutoTableLayout(this)); 96 } 97 98 // If border was changed, invalidate collapsed borders cache. 99 if (!needsLayout() && oldStyle && oldStyle->border() != style()->border()) 100 invalidateCollapsedBorders(); 101} 102 103static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before) 104{ 105 if (!before || !ptr) 106 return; 107 RenderObject* o = before->previousSibling(); 108 while (o && o != ptr) 109 o = o->previousSibling(); 110 if (!o) 111 ptr = 0; 112} 113 114void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) 115{ 116 bool wrapInAnonymousSection = !child->isOutOfFlowPositioned(); 117 118 if (child->isTableCaption()) 119 wrapInAnonymousSection = false; 120 else if (child->isRenderTableCol()) { 121 m_hasColElements = true; 122 wrapInAnonymousSection = false; 123 } else if (child->isTableSection()) { 124 switch (child->style()->display()) { 125 case TABLE_HEADER_GROUP: 126 resetSectionPointerIfNotBefore(m_head, beforeChild); 127 if (!m_head) { 128 m_head = toRenderTableSection(child); 129 } else { 130 resetSectionPointerIfNotBefore(m_firstBody, beforeChild); 131 if (!m_firstBody) 132 m_firstBody = toRenderTableSection(child); 133 } 134 wrapInAnonymousSection = false; 135 break; 136 case TABLE_FOOTER_GROUP: 137 resetSectionPointerIfNotBefore(m_foot, beforeChild); 138 if (!m_foot) { 139 m_foot = toRenderTableSection(child); 140 wrapInAnonymousSection = false; 141 break; 142 } 143 // Fall through. 144 case TABLE_ROW_GROUP: 145 resetSectionPointerIfNotBefore(m_firstBody, beforeChild); 146 if (!m_firstBody) 147 m_firstBody = toRenderTableSection(child); 148 wrapInAnonymousSection = false; 149 break; 150 default: 151 ASSERT_NOT_REACHED(); 152 } 153 } else if (child->isTableCell() || child->isTableRow()) 154 wrapInAnonymousSection = true; 155 else 156 wrapInAnonymousSection = true; 157 158 if (child->isTableSection()) 159 setNeedsSectionRecalc(); 160 161 if (!wrapInAnonymousSection) { 162 if (beforeChild && beforeChild->parent() != this) 163 beforeChild = splitAnonymousBoxesAroundChild(beforeChild); 164 165 RenderBox::addChild(child, beforeChild); 166 return; 167 } 168 169 if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous() && !lastChild()->isBeforeContent()) { 170 lastChild()->addChild(child); 171 return; 172 } 173 174 if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) { 175 RenderObject* section = beforeChild->previousSibling(); 176 if (section && section->isTableSection() && section->isAnonymous()) { 177 section->addChild(child); 178 return; 179 } 180 } 181 182 RenderObject* lastBox = beforeChild; 183 while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION && lastBox->style()->display() != TABLE_COLUMN_GROUP) 184 lastBox = lastBox->parent(); 185 if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox) && lastBox->isTableSection()) { 186 if (beforeChild == lastBox) 187 beforeChild = lastBox->firstChild(); 188 lastBox->addChild(child, beforeChild); 189 return; 190 } 191 192 if (beforeChild && !beforeChild->isTableSection() && beforeChild->style()->display() != TABLE_CAPTION && beforeChild->style()->display() != TABLE_COLUMN_GROUP) 193 beforeChild = 0; 194 195 RenderTableSection* section = RenderTableSection::createAnonymousWithParentRenderer(this); 196 addChild(section, beforeChild); 197 section->addChild(child); 198} 199 200void RenderTable::addCaption(const RenderTableCaption* caption) 201{ 202 ASSERT(m_captions.find(caption) == notFound); 203 m_captions.append(const_cast<RenderTableCaption*>(caption)); 204} 205 206void RenderTable::removeCaption(const RenderTableCaption* oldCaption) 207{ 208 size_t index = m_captions.find(oldCaption); 209 ASSERT(index != notFound); 210 if (index == notFound) 211 return; 212 213 m_captions.remove(index); 214} 215 216void RenderTable::invalidateCachedColumns() 217{ 218 m_columnRenderersValid = false; 219 m_columnRenderers.resize(0); 220} 221 222void RenderTable::addColumn(const RenderTableCol*) 223{ 224 invalidateCachedColumns(); 225} 226 227void RenderTable::removeColumn(const RenderTableCol*) 228{ 229 invalidateCachedColumns(); 230 // We don't really need to recompute our sections, but we need to update our 231 // column count and whether we have a column. Currently, we only have one 232 // size-fit-all flag but we may have to consider splitting it. 233 setNeedsSectionRecalc(); 234} 235 236void RenderTable::updateLogicalWidth() 237{ 238 recalcSectionsIfNeeded(); 239 240 if (isOutOfFlowPositioned()) { 241 LogicalExtentComputedValues computedValues; 242 computePositionedLogicalWidth(computedValues); 243 setLogicalWidth(computedValues.m_extent); 244 setLogicalLeft(computedValues.m_position); 245 setMarginStart(computedValues.m_margins.m_start); 246 setMarginEnd(computedValues.m_margins.m_end); 247 } 248 249 RenderBlock* cb = containingBlock(); 250 RenderView* renderView = view(); 251 252 LayoutUnit availableLogicalWidth = containingBlockLogicalWidthForContent(); 253 bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode(); 254 LayoutUnit containerWidthInInlineDirection = hasPerpendicularContainingBlock ? perpendicularContainingBlockLogicalHeight() : availableLogicalWidth; 255 256 Length styleLogicalWidth = style()->logicalWidth(); 257 if ((styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive()) || styleLogicalWidth.isIntrinsic()) 258 setLogicalWidth(convertStyleLogicalWidthToComputedWidth(styleLogicalWidth, containerWidthInInlineDirection)); 259 else { 260 // Subtract out any fixed margins from our available width for auto width tables. 261 LayoutUnit marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth, renderView); 262 LayoutUnit marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth, renderView); 263 LayoutUnit marginTotal = marginStart + marginEnd; 264 265 // Subtract out our margins to get the available content width. 266 LayoutUnit availableContentLogicalWidth = max<LayoutUnit>(0, containerWidthInInlineDirection - marginTotal); 267 if (shrinkToAvoidFloats() && cb->containsFloats() && !hasPerpendicularContainingBlock) { 268 // FIXME: Work with regions someday. 269 availableContentLogicalWidth = shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, cb, 0); 270 } 271 272 // Ensure we aren't bigger than our available width. 273 setLogicalWidth(min<int>(availableContentLogicalWidth, maxPreferredLogicalWidth())); 274 } 275 276 // Ensure we aren't smaller than our min preferred width. 277 setLogicalWidth(max<int>(logicalWidth(), minPreferredLogicalWidth())); 278 279 280 // Ensure we aren't bigger than our max-width style. 281 Length styleMaxLogicalWidth = style()->logicalMaxWidth(); 282 if ((styleMaxLogicalWidth.isSpecified() && !styleMaxLogicalWidth.isNegative()) || styleMaxLogicalWidth.isIntrinsic()) { 283 LayoutUnit computedMaxLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMaxLogicalWidth, availableLogicalWidth); 284 setLogicalWidth(min<int>(logicalWidth(), computedMaxLogicalWidth)); 285 } 286 287 // Ensure we aren't smaller than our min-width style. 288 Length styleMinLogicalWidth = style()->logicalMinWidth(); 289 if ((styleMinLogicalWidth.isSpecified() && !styleMinLogicalWidth.isNegative()) || styleMinLogicalWidth.isIntrinsic()) { 290 LayoutUnit computedMinLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMinLogicalWidth, availableLogicalWidth); 291 setLogicalWidth(max<int>(logicalWidth(), computedMinLogicalWidth)); 292 } 293 294 // Finally, with our true width determined, compute our margins for real. 295 setMarginStart(0); 296 setMarginEnd(0); 297 if (!hasPerpendicularContainingBlock) { 298 LayoutUnit containerLogicalWidthForAutoMargins = availableLogicalWidth; 299 if (avoidsFloats() && cb->containsFloats()) 300 containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidthInRegion(0); // FIXME: Work with regions someday. 301 ComputedMarginValues marginValues; 302 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() == style()->isLeftToRightDirection(); 303 computeInlineDirectionMargins(cb, containerLogicalWidthForAutoMargins, logicalWidth(), 304 hasInvertedDirection ? marginValues.m_start : marginValues.m_end, 305 hasInvertedDirection ? marginValues.m_end : marginValues.m_start); 306 setMarginStart(marginValues.m_start); 307 setMarginEnd(marginValues.m_end); 308 } else { 309 setMarginStart(minimumValueForLength(style()->marginStart(), availableLogicalWidth, renderView)); 310 setMarginEnd(minimumValueForLength(style()->marginEnd(), availableLogicalWidth, renderView)); 311 } 312} 313 314// This method takes a RenderStyle's logical width, min-width, or max-width length and computes its actual value. 315LayoutUnit RenderTable::convertStyleLogicalWidthToComputedWidth(const Length& styleLogicalWidth, LayoutUnit availableWidth) 316{ 317 if (styleLogicalWidth.isIntrinsic()) 318 return computeIntrinsicLogicalWidthUsing(styleLogicalWidth, availableWidth, bordersPaddingAndSpacingInRowDirection()); 319 320 // HTML tables' width styles already include borders and paddings, but CSS tables' width styles do not. 321 LayoutUnit borders = 0; 322 bool isCSSTable = !node() || !node()->hasTagName(tableTag); 323 if (isCSSTable && styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive() && style()->boxSizing() == CONTENT_BOX) 324 borders = borderStart() + borderEnd() + (collapseBorders() ? LayoutUnit() : paddingStart() + paddingEnd()); 325 326 return minimumValueForLength(styleLogicalWidth, availableWidth, view()) + borders; 327} 328 329LayoutUnit RenderTable::convertStyleLogicalHeightToComputedHeight(const Length& styleLogicalHeight) 330{ 331 LayoutUnit computedLogicalHeight = 0; 332 if (styleLogicalHeight.isFixed()) { 333 // HTML tables size as though CSS height includes border/padding, CSS tables do not. 334 LayoutUnit borders = LayoutUnit(); 335 // FIXME: We cannot apply box-sizing: content-box on <table> which other browsers allow. 336 if ((node() && node()->hasTagName(tableTag)) || style()->boxSizing() == BORDER_BOX) { 337 LayoutUnit borderAndPaddingBefore = borderBefore() + (collapseBorders() ? LayoutUnit() : paddingBefore()); 338 LayoutUnit borderAndPaddingAfter = borderAfter() + (collapseBorders() ? LayoutUnit() : paddingAfter()); 339 borders = borderAndPaddingBefore + borderAndPaddingAfter; 340 } 341 computedLogicalHeight = styleLogicalHeight.value() - borders; 342 } else if (styleLogicalHeight.isPercent()) 343 computedLogicalHeight = computePercentageLogicalHeight(styleLogicalHeight); 344 else if (styleLogicalHeight.isViewportPercentage()) 345 computedLogicalHeight = minimumValueForLength(styleLogicalHeight, 0, view()); 346 else 347 ASSERT_NOT_REACHED(); 348 return max<LayoutUnit>(0, computedLogicalHeight); 349} 350 351void RenderTable::layoutCaption(RenderTableCaption* caption) 352{ 353 LayoutRect captionRect(caption->frameRect()); 354 355 if (caption->needsLayout()) { 356 // The margins may not be available but ensure the caption is at least located beneath any previous sibling caption 357 // so that it does not mistakenly think any floats in the previous caption intrude into it. 358 caption->setLogicalLocation(LayoutPoint(caption->marginStart(), caption->marginBefore() + logicalHeight())); 359 // If RenderTableCaption ever gets a layout() function, use it here. 360 caption->layoutIfNeeded(); 361 } 362 // Apply the margins to the location now that they are definitely available from layout 363 caption->setLogicalLocation(LayoutPoint(caption->marginStart(), caption->marginBefore() + logicalHeight())); 364 365 if (!selfNeedsLayout() && caption->checkForRepaintDuringLayout()) 366 caption->repaintDuringLayoutIfMoved(captionRect); 367 368 setLogicalHeight(logicalHeight() + caption->logicalHeight() + caption->marginBefore() + caption->marginAfter()); 369} 370 371void RenderTable::distributeExtraLogicalHeight(int extraLogicalHeight) 372{ 373 if (extraLogicalHeight <= 0) 374 return; 375 376 // FIXME: Distribute the extra logical height between all table sections instead of giving it all to the first one. 377 if (RenderTableSection* section = firstBody()) 378 extraLogicalHeight -= section->distributeExtraLogicalHeightToRows(extraLogicalHeight); 379 380 // FIXME: We really would like to enable this ASSERT to ensure that all the extra space has been distributed. 381 // However our current distribution algorithm does not round properly and thus we can have some remaining height. 382 // ASSERT(!topSection() || !extraLogicalHeight); 383} 384 385void RenderTable::simplifiedNormalFlowLayout() 386{ 387 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) { 388 section->layoutIfNeeded(); 389 section->computeOverflowFromCells(); 390 } 391} 392 393void RenderTable::layout() 394{ 395 StackStats::LayoutCheckPoint layoutCheckPoint; 396 ASSERT(needsLayout()); 397 398 if (simplifiedLayout()) 399 return; 400 401 recalcSectionsIfNeeded(); 402 // FIXME: We should do this recalc lazily in borderStart/borderEnd so that we don't have to make sure 403 // to call this before we call borderStart/borderEnd to avoid getting a stale value. 404 recalcBordersInRowDirection(); 405 406 LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); 407 LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); 408 409 setLogicalHeight(0); 410 411 initMaxMarginValues(); 412 413 LayoutUnit oldLogicalWidth = logicalWidth(); 414 updateLogicalWidth(); 415 416 if (logicalWidth() != oldLogicalWidth) { 417 for (unsigned i = 0; i < m_captions.size(); i++) 418 m_captions[i]->setNeedsLayout(true, MarkOnlyThis); 419 } 420 // FIXME: The optimisation below doesn't work since the internal table 421 // layout could have changed. we need to add a flag to the table 422 // layout that tells us if something has changed in the min max 423 // calculations to do it correctly. 424// if ( oldWidth != width() || columns.size() + 1 != columnPos.size() ) 425 m_tableLayout->layout(); 426 427 LayoutUnit totalSectionLogicalHeight = 0; 428 LayoutUnit oldTableLogicalTop = 0; 429 for (unsigned i = 0; i < m_captions.size(); i++) 430 oldTableLogicalTop += m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter(); 431 432 bool collapsing = collapseBorders(); 433 434 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 435 if (child->isTableSection()) { 436 RenderTableSection* section = toRenderTableSection(child); 437 if (m_columnLogicalWidthChanged) 438 section->setChildNeedsLayout(true, MarkOnlyThis); 439 section->layoutIfNeeded(); 440 totalSectionLogicalHeight += section->calcRowLogicalHeight(); 441 if (collapsing) 442 section->recalcOuterBorder(); 443 ASSERT(!section->needsLayout()); 444 } else if (child->isRenderTableCol()) { 445 child->layoutIfNeeded(); 446 ASSERT(!child->needsLayout()); 447 } 448 } 449 450 // If any table section moved vertically, we will just repaint everything from that 451 // section down (it is quite unlikely that any of the following sections 452 // did not shift). 453 bool sectionMoved = false; 454 LayoutUnit movedSectionLogicalTop = 0; 455 456 // FIXME: Collapse caption margin. 457 if (!m_captions.isEmpty()) { 458 for (unsigned i = 0; i < m_captions.size(); i++) { 459 if (m_captions[i]->style()->captionSide() == CAPBOTTOM) 460 continue; 461 layoutCaption(m_captions[i]); 462 } 463 if (logicalHeight() != oldTableLogicalTop) { 464 sectionMoved = true; 465 movedSectionLogicalTop = min(logicalHeight(), oldTableLogicalTop); 466 } 467 } 468 469 LayoutUnit borderAndPaddingBefore = borderBefore() + (collapsing ? LayoutUnit() : paddingBefore()); 470 LayoutUnit borderAndPaddingAfter = borderAfter() + (collapsing ? LayoutUnit() : paddingAfter()); 471 472 setLogicalHeight(logicalHeight() + borderAndPaddingBefore); 473 474 if (!isOutOfFlowPositioned()) 475 updateLogicalHeight(); 476 477 LayoutUnit computedLogicalHeight = 0; 478 479 Length logicalHeightLength = style()->logicalHeight(); 480 if (logicalHeightLength.isSpecified() && logicalHeightLength.isPositive()) 481 computedLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalHeightLength); 482 483 Length logicalMaxHeightLength = style()->logicalMaxHeight(); 484 if (logicalMaxHeightLength.isSpecified() && !logicalMaxHeightLength.isNegative()) { 485 LayoutUnit computedMaxLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMaxHeightLength); 486 computedLogicalHeight = min(computedLogicalHeight, computedMaxLogicalHeight); 487 } 488 489 Length logicalMinHeightLength = style()->logicalMinHeight(); 490 if (logicalMinHeightLength.isSpecified() && !logicalMinHeightLength.isNegative()) { 491 LayoutUnit computedMinLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMinHeightLength); 492 computedLogicalHeight = max(computedLogicalHeight, computedMinLogicalHeight); 493 } 494 495 distributeExtraLogicalHeight(floorToInt(computedLogicalHeight - totalSectionLogicalHeight)); 496 497 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) 498 section->layoutRows(); 499 500 if (!topSection() && computedLogicalHeight > totalSectionLogicalHeight && !document()->inQuirksMode()) { 501 // Completely empty tables (with no sections or anything) should at least honor specified height 502 // in strict mode. 503 setLogicalHeight(logicalHeight() + computedLogicalHeight); 504 } 505 506 LayoutUnit sectionLogicalLeft = style()->isLeftToRightDirection() ? borderStart() : borderEnd(); 507 if (!collapsing) 508 sectionLogicalLeft += style()->isLeftToRightDirection() ? paddingStart() : paddingEnd(); 509 510 // position the table sections 511 RenderTableSection* section = topSection(); 512 while (section) { 513 if (!sectionMoved && section->logicalTop() != logicalHeight()) { 514 sectionMoved = true; 515 movedSectionLogicalTop = min(logicalHeight(), section->logicalTop()) + (style()->isHorizontalWritingMode() ? section->visualOverflowRect().y() : section->visualOverflowRect().x()); 516 } 517 section->setLogicalLocation(LayoutPoint(sectionLogicalLeft, logicalHeight())); 518 519 setLogicalHeight(logicalHeight() + section->logicalHeight()); 520 section = sectionBelow(section); 521 } 522 523 setLogicalHeight(logicalHeight() + borderAndPaddingAfter); 524 525 for (unsigned i = 0; i < m_captions.size(); i++) { 526 if (m_captions[i]->style()->captionSide() != CAPBOTTOM) 527 continue; 528 layoutCaption(m_captions[i]); 529 } 530 531 if (isOutOfFlowPositioned()) 532 updateLogicalHeight(); 533 534 // table can be containing block of positioned elements. 535 // FIXME: Only pass true if width or height changed. 536 layoutPositionedObjects(true); 537 538 updateLayerTransform(); 539 540 // Layout was changed, so probably borders too. 541 invalidateCollapsedBorders(); 542 543 computeOverflow(clientLogicalBottom()); 544 545 statePusher.pop(); 546 547 if (view()->layoutState()->pageLogicalHeight()) 548 setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(this, logicalTop())); 549 550 bool didFullRepaint = repainter.repaintAfterLayout(); 551 // Repaint with our new bounds if they are different from our old bounds. 552 if (!didFullRepaint && sectionMoved) { 553 if (style()->isHorizontalWritingMode()) 554 repaintRectangle(LayoutRect(visualOverflowRect().x(), movedSectionLogicalTop, visualOverflowRect().width(), visualOverflowRect().maxY() - movedSectionLogicalTop)); 555 else 556 repaintRectangle(LayoutRect(movedSectionLogicalTop, visualOverflowRect().y(), visualOverflowRect().maxX() - movedSectionLogicalTop, visualOverflowRect().height())); 557 } 558 559 m_columnLogicalWidthChanged = false; 560 setNeedsLayout(false); 561} 562 563// Collect all the unique border values that we want to paint in a sorted list. 564void RenderTable::recalcCollapsedBorders() 565{ 566 if (m_collapsedBordersValid) 567 return; 568 m_collapsedBordersValid = true; 569 m_collapsedBorders.clear(); 570 for (RenderObject* section = firstChild(); section; section = section->nextSibling()) { 571 if (!section->isTableSection()) 572 continue; 573 for (RenderObject* row = section->firstChild(); row; row = row->nextSibling()) { 574 if (!row->isTableRow()) 575 continue; 576 for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { 577 if (!cell->isTableCell()) 578 continue; 579 ASSERT(toRenderTableCell(cell)->table() == this); 580 toRenderTableCell(cell)->collectBorderValues(m_collapsedBorders); 581 } 582 } 583 } 584 RenderTableCell::sortBorderValues(m_collapsedBorders); 585} 586 587 588void RenderTable::addOverflowFromChildren() 589{ 590 // Add overflow from borders. 591 // Technically it's odd that we are incorporating the borders into layout overflow, which is only supposed to be about overflow from our 592 // descendant objects, but since tables don't support overflow:auto, this works out fine. 593 if (collapseBorders()) { 594 int rightBorderOverflow = width() + outerBorderRight() - borderRight(); 595 int leftBorderOverflow = borderLeft() - outerBorderLeft(); 596 int bottomBorderOverflow = height() + outerBorderBottom() - borderBottom(); 597 int topBorderOverflow = borderTop() - outerBorderTop(); 598 IntRect borderOverflowRect(leftBorderOverflow, topBorderOverflow, rightBorderOverflow - leftBorderOverflow, bottomBorderOverflow - topBorderOverflow); 599 if (borderOverflowRect != pixelSnappedBorderBoxRect()) { 600 addLayoutOverflow(borderOverflowRect); 601 addVisualOverflow(borderOverflowRect); 602 } 603 } 604 605 // Add overflow from our caption. 606 for (unsigned i = 0; i < m_captions.size(); i++) 607 addOverflowFromChild(m_captions[i]); 608 609 // Add overflow from our sections. 610 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) 611 addOverflowFromChild(section); 612} 613 614void RenderTable::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 615{ 616 LayoutPoint adjustedPaintOffset = paintOffset + location(); 617 618 PaintPhase paintPhase = paintInfo.phase; 619 620 if (!isRoot()) { 621 LayoutRect overflowBox = visualOverflowRect(); 622 flipForWritingMode(overflowBox); 623 overflowBox.inflate(maximalOutlineSize(paintInfo.phase)); 624 overflowBox.moveBy(adjustedPaintOffset); 625 if (!overflowBox.intersects(paintInfo.rect)) 626 return; 627 } 628 629 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset); 630 paintObject(paintInfo, adjustedPaintOffset); 631 if (pushedClip) 632 popContentsClip(paintInfo, paintPhase, adjustedPaintOffset); 633} 634 635void RenderTable::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 636{ 637 PaintPhase paintPhase = paintInfo.phase; 638 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE) 639 paintBoxDecorations(paintInfo, paintOffset); 640 641 if (paintPhase == PaintPhaseMask) { 642 paintMask(paintInfo, paintOffset); 643 return; 644 } 645 646 // We're done. We don't bother painting any children. 647 if (paintPhase == PaintPhaseBlockBackground) 648 return; 649 650 // We don't paint our own background, but we do let the kids paint their backgrounds. 651 if (paintPhase == PaintPhaseChildBlockBackgrounds) 652 paintPhase = PaintPhaseChildBlockBackground; 653 654 PaintInfo info(paintInfo); 655 info.phase = paintPhase; 656 info.updateSubtreePaintRootForChildren(this); 657 658 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 659 if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) { 660 LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), paintOffset); 661 child->paint(info, childPoint); 662 } 663 } 664 665 if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) { 666 recalcCollapsedBorders(); 667 // Using our cached sorted styles, we then do individual passes, 668 // painting each style of border from lowest precedence to highest precedence. 669 info.phase = PaintPhaseCollapsedTableBorders; 670 size_t count = m_collapsedBorders.size(); 671 for (size_t i = 0; i < count; ++i) { 672 m_currentBorder = &m_collapsedBorders[i]; 673 for (RenderTableSection* section = bottomSection(); section; section = sectionAbove(section)) { 674 LayoutPoint childPoint = flipForWritingModeForChild(section, paintOffset); 675 section->paint(info, childPoint); 676 } 677 } 678 m_currentBorder = 0; 679 } 680 681 // Paint outline. 682 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE) 683 paintOutline(paintInfo.context, LayoutRect(paintOffset, size())); 684} 685 686void RenderTable::subtractCaptionRect(LayoutRect& rect) const 687{ 688 for (unsigned i = 0; i < m_captions.size(); i++) { 689 LayoutUnit captionLogicalHeight = m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter(); 690 bool captionIsBefore = (m_captions[i]->style()->captionSide() != CAPBOTTOM) ^ style()->isFlippedBlocksWritingMode(); 691 if (style()->isHorizontalWritingMode()) { 692 rect.setHeight(rect.height() - captionLogicalHeight); 693 if (captionIsBefore) 694 rect.move(0, captionLogicalHeight); 695 } else { 696 rect.setWidth(rect.width() - captionLogicalHeight); 697 if (captionIsBefore) 698 rect.move(captionLogicalHeight, 0); 699 } 700 } 701} 702 703void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 704{ 705 if (!paintInfo.shouldPaintWithinRoot(this)) 706 return; 707 708 LayoutRect rect(paintOffset, size()); 709 subtractCaptionRect(rect); 710 711 BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context); 712 if (!boxShadowShouldBeAppliedToBackground(bleedAvoidance)) 713 paintBoxShadow(paintInfo, rect, style(), Normal); 714 paintBackground(paintInfo, rect, bleedAvoidance); 715 paintBoxShadow(paintInfo, rect, style(), Inset); 716 717 if (style()->hasBorder() && !collapseBorders()) 718 paintBorder(paintInfo, rect, style()); 719} 720 721void RenderTable::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 722{ 723 if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) 724 return; 725 726 LayoutRect rect(paintOffset, size()); 727 subtractCaptionRect(rect); 728 729 paintMaskImages(paintInfo, rect); 730} 731 732void RenderTable::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) const 733{ 734 recalcSectionsIfNeeded(); 735 // FIXME: Do the recalc in borderStart/borderEnd and make those const_cast this call. 736 // Then m_borderStart/m_borderEnd will be transparent a cache and it removes the possibility 737 // of reading out stale values. 738 const_cast<RenderTable*>(this)->recalcBordersInRowDirection(); 739 // FIXME: Restructure the table layout code so that we can make this method const. 740 const_cast<RenderTable*>(this)->m_tableLayout->computeIntrinsicLogicalWidths(minWidth, maxWidth); 741 742 // FIXME: We should include captions widths here like we do in computePreferredLogicalWidths. 743} 744 745void RenderTable::computePreferredLogicalWidths() 746{ 747 ASSERT(preferredLogicalWidthsDirty()); 748 749 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); 750 751 int bordersPaddingAndSpacing = bordersPaddingAndSpacingInRowDirection(); 752 m_minPreferredLogicalWidth += bordersPaddingAndSpacing; 753 m_maxPreferredLogicalWidth += bordersPaddingAndSpacing; 754 755 m_tableLayout->applyPreferredLogicalWidthQuirks(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); 756 757 for (unsigned i = 0; i < m_captions.size(); i++) 758 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_captions[i]->minPreferredLogicalWidth()); 759 760 RenderStyle* styleToUse = style(); 761 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for min-width. 762 if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) { 763 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); 764 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); 765 } 766 767 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for maxWidth. 768 if (styleToUse->logicalMaxWidth().isFixed()) { 769 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); 770 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); 771 } 772 773 // FIXME: We should be adding borderAndPaddingLogicalWidth here, but m_tableLayout->computePreferredLogicalWidths already does, 774 // so a bunch of tests break doing this naively. 775 setPreferredLogicalWidthsDirty(false); 776} 777 778RenderTableSection* RenderTable::topNonEmptySection() const 779{ 780 RenderTableSection* section = topSection(); 781 if (section && !section->numRows()) 782 section = sectionBelow(section, SkipEmptySections); 783 return section; 784} 785 786void RenderTable::splitColumn(unsigned position, unsigned firstSpan) 787{ 788 // We split the column at "position", taking "firstSpan" cells from the span. 789 ASSERT(m_columns[position].span > firstSpan); 790 m_columns.insert(position, ColumnStruct(firstSpan)); 791 m_columns[position + 1].span -= firstSpan; 792 793 // Propagate the change in our columns representation to the sections that don't need 794 // cell recalc. If they do, they will be synced up directly with m_columns later. 795 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 796 if (!child->isTableSection()) 797 continue; 798 799 RenderTableSection* section = toRenderTableSection(child); 800 if (section->needsCellRecalc()) 801 continue; 802 803 section->splitColumn(position, firstSpan); 804 } 805 806 m_columnPos.grow(numEffCols() + 1); 807} 808 809void RenderTable::appendColumn(unsigned span) 810{ 811 unsigned newColumnIndex = m_columns.size(); 812 m_columns.append(ColumnStruct(span)); 813 814 // Propagate the change in our columns representation to the sections that don't need 815 // cell recalc. If they do, they will be synced up directly with m_columns later. 816 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 817 if (!child->isTableSection()) 818 continue; 819 820 RenderTableSection* section = toRenderTableSection(child); 821 if (section->needsCellRecalc()) 822 continue; 823 824 section->appendColumn(newColumnIndex); 825 } 826 827 m_columnPos.grow(numEffCols() + 1); 828} 829 830RenderTableCol* RenderTable::firstColumn() const 831{ 832 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 833 if (child->isRenderTableCol()) 834 return toRenderTableCol(child); 835 836 // We allow only table-captions before columns or column-groups. 837 if (!child->isTableCaption()) 838 return 0; 839 } 840 841 return 0; 842} 843 844void RenderTable::updateColumnCache() const 845{ 846 ASSERT(m_hasColElements); 847 ASSERT(m_columnRenderers.isEmpty()); 848 ASSERT(!m_columnRenderersValid); 849 850 for (RenderTableCol* columnRenderer = firstColumn(); columnRenderer; columnRenderer = columnRenderer->nextColumn()) { 851 if (columnRenderer->isTableColumnGroupWithColumnChildren()) 852 continue; 853 m_columnRenderers.append(columnRenderer); 854 } 855 m_columnRenderersValid = true; 856} 857 858RenderTableCol* RenderTable::slowColElement(unsigned col, bool* startEdge, bool* endEdge) const 859{ 860 ASSERT(m_hasColElements); 861 862 if (!m_columnRenderersValid) 863 updateColumnCache(); 864 865 unsigned columnCount = 0; 866 for (unsigned i = 0; i < m_columnRenderers.size(); i++) { 867 RenderTableCol* columnRenderer = m_columnRenderers[i]; 868 unsigned span = columnRenderer->span(); 869 unsigned startCol = columnCount; 870 ASSERT(span >= 1); 871 unsigned endCol = columnCount + span - 1; 872 columnCount += span; 873 if (columnCount > col) { 874 if (startEdge) 875 *startEdge = startCol == col; 876 if (endEdge) 877 *endEdge = endCol == col; 878 return columnRenderer; 879 } 880 } 881 return 0; 882} 883 884void RenderTable::recalcSections() const 885{ 886 ASSERT(m_needsSectionRecalc); 887 888 m_head = 0; 889 m_foot = 0; 890 m_firstBody = 0; 891 m_hasColElements = false; 892 893 // We need to get valid pointers to caption, head, foot and first body again 894 RenderObject* nextSibling; 895 for (RenderObject* child = firstChild(); child; child = nextSibling) { 896 nextSibling = child->nextSibling(); 897 switch (child->style()->display()) { 898 case TABLE_COLUMN: 899 case TABLE_COLUMN_GROUP: 900 m_hasColElements = true; 901 break; 902 case TABLE_HEADER_GROUP: 903 if (child->isTableSection()) { 904 RenderTableSection* section = toRenderTableSection(child); 905 if (!m_head) 906 m_head = section; 907 else if (!m_firstBody) 908 m_firstBody = section; 909 section->recalcCellsIfNeeded(); 910 } 911 break; 912 case TABLE_FOOTER_GROUP: 913 if (child->isTableSection()) { 914 RenderTableSection* section = toRenderTableSection(child); 915 if (!m_foot) 916 m_foot = section; 917 else if (!m_firstBody) 918 m_firstBody = section; 919 section->recalcCellsIfNeeded(); 920 } 921 break; 922 case TABLE_ROW_GROUP: 923 if (child->isTableSection()) { 924 RenderTableSection* section = toRenderTableSection(child); 925 if (!m_firstBody) 926 m_firstBody = section; 927 section->recalcCellsIfNeeded(); 928 } 929 break; 930 default: 931 break; 932 } 933 } 934 935 // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section) 936 unsigned maxCols = 0; 937 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 938 if (child->isTableSection()) { 939 RenderTableSection* section = toRenderTableSection(child); 940 unsigned sectionCols = section->numColumns(); 941 if (sectionCols > maxCols) 942 maxCols = sectionCols; 943 } 944 } 945 946 m_columns.resize(maxCols); 947 m_columnPos.resize(maxCols + 1); 948 949 ASSERT(selfNeedsLayout()); 950 951 m_needsSectionRecalc = false; 952} 953 954int RenderTable::calcBorderStart() const 955{ 956 if (!collapseBorders()) 957 return RenderBlock::borderStart(); 958 959 // Determined by the first cell of the first row. See the CSS 2.1 spec, section 17.6.2. 960 if (!numEffCols()) 961 return 0; 962 963 unsigned borderWidth = 0; 964 965 const BorderValue& tableStartBorder = style()->borderStart(); 966 if (tableStartBorder.style() == BHIDDEN) 967 return 0; 968 if (tableStartBorder.style() > BHIDDEN) 969 borderWidth = tableStartBorder.width(); 970 971 if (RenderTableCol* column = colElement(0)) { 972 // FIXME: We don't account for direction on columns and column groups. 973 const BorderValue& columnAdjoiningBorder = column->style()->borderStart(); 974 if (columnAdjoiningBorder.style() == BHIDDEN) 975 return 0; 976 if (columnAdjoiningBorder.style() > BHIDDEN) 977 borderWidth = max(borderWidth, columnAdjoiningBorder.width()); 978 // FIXME: This logic doesn't properly account for the first column in the first column-group case. 979 } 980 981 if (const RenderTableSection* topNonEmptySection = this->topNonEmptySection()) { 982 const BorderValue& sectionAdjoiningBorder = topNonEmptySection->borderAdjoiningTableStart(); 983 if (sectionAdjoiningBorder.style() == BHIDDEN) 984 return 0; 985 986 if (sectionAdjoiningBorder.style() > BHIDDEN) 987 borderWidth = max(borderWidth, sectionAdjoiningBorder.width()); 988 989 if (const RenderTableCell* adjoiningStartCell = topNonEmptySection->firstRowCellAdjoiningTableStart()) { 990 // FIXME: Make this work with perpendicular and flipped cells. 991 const BorderValue& startCellAdjoiningBorder = adjoiningStartCell->borderAdjoiningTableStart(); 992 if (startCellAdjoiningBorder.style() == BHIDDEN) 993 return 0; 994 995 const BorderValue& firstRowAdjoiningBorder = adjoiningStartCell->row()->borderAdjoiningTableStart(); 996 if (firstRowAdjoiningBorder.style() == BHIDDEN) 997 return 0; 998 999 if (startCellAdjoiningBorder.style() > BHIDDEN) 1000 borderWidth = max(borderWidth, startCellAdjoiningBorder.width()); 1001 if (firstRowAdjoiningBorder.style() > BHIDDEN) 1002 borderWidth = max(borderWidth, firstRowAdjoiningBorder.width()); 1003 } 1004 } 1005 return (borderWidth + (style()->isLeftToRightDirection() ? 0 : 1)) / 2; 1006} 1007 1008int RenderTable::calcBorderEnd() const 1009{ 1010 if (!collapseBorders()) 1011 return RenderBlock::borderEnd(); 1012 1013 // Determined by the last cell of the first row. See the CSS 2.1 spec, section 17.6.2. 1014 if (!numEffCols()) 1015 return 0; 1016 1017 unsigned borderWidth = 0; 1018 1019 const BorderValue& tableEndBorder = style()->borderEnd(); 1020 if (tableEndBorder.style() == BHIDDEN) 1021 return 0; 1022 if (tableEndBorder.style() > BHIDDEN) 1023 borderWidth = tableEndBorder.width(); 1024 1025 unsigned endColumn = numEffCols() - 1; 1026 if (RenderTableCol* column = colElement(endColumn)) { 1027 // FIXME: We don't account for direction on columns and column groups. 1028 const BorderValue& columnAdjoiningBorder = column->style()->borderEnd(); 1029 if (columnAdjoiningBorder.style() == BHIDDEN) 1030 return 0; 1031 if (columnAdjoiningBorder.style() > BHIDDEN) 1032 borderWidth = max(borderWidth, columnAdjoiningBorder.width()); 1033 // FIXME: This logic doesn't properly account for the last column in the last column-group case. 1034 } 1035 1036 if (const RenderTableSection* topNonEmptySection = this->topNonEmptySection()) { 1037 const BorderValue& sectionAdjoiningBorder = topNonEmptySection->borderAdjoiningTableEnd(); 1038 if (sectionAdjoiningBorder.style() == BHIDDEN) 1039 return 0; 1040 1041 if (sectionAdjoiningBorder.style() > BHIDDEN) 1042 borderWidth = max(borderWidth, sectionAdjoiningBorder.width()); 1043 1044 if (const RenderTableCell* adjoiningEndCell = topNonEmptySection->firstRowCellAdjoiningTableEnd()) { 1045 // FIXME: Make this work with perpendicular and flipped cells. 1046 const BorderValue& endCellAdjoiningBorder = adjoiningEndCell->borderAdjoiningTableEnd(); 1047 if (endCellAdjoiningBorder.style() == BHIDDEN) 1048 return 0; 1049 1050 const BorderValue& firstRowAdjoiningBorder = adjoiningEndCell->row()->borderAdjoiningTableEnd(); 1051 if (firstRowAdjoiningBorder.style() == BHIDDEN) 1052 return 0; 1053 1054 if (endCellAdjoiningBorder.style() > BHIDDEN) 1055 borderWidth = max(borderWidth, endCellAdjoiningBorder.width()); 1056 if (firstRowAdjoiningBorder.style() > BHIDDEN) 1057 borderWidth = max(borderWidth, firstRowAdjoiningBorder.width()); 1058 } 1059 } 1060 return (borderWidth + (style()->isLeftToRightDirection() ? 1 : 0)) / 2; 1061} 1062 1063void RenderTable::recalcBordersInRowDirection() 1064{ 1065 // FIXME: We need to compute the collapsed before / after borders in the same fashion. 1066 m_borderStart = calcBorderStart(); 1067 m_borderEnd = calcBorderEnd(); 1068} 1069 1070int RenderTable::borderBefore() const 1071{ 1072 if (collapseBorders()) { 1073 recalcSectionsIfNeeded(); 1074 return outerBorderBefore(); 1075 } 1076 return RenderBlock::borderBefore(); 1077} 1078 1079int RenderTable::borderAfter() const 1080{ 1081 if (collapseBorders()) { 1082 recalcSectionsIfNeeded(); 1083 return outerBorderAfter(); 1084 } 1085 return RenderBlock::borderAfter(); 1086} 1087 1088int RenderTable::outerBorderBefore() const 1089{ 1090 if (!collapseBorders()) 1091 return 0; 1092 int borderWidth = 0; 1093 if (RenderTableSection* topSection = this->topSection()) { 1094 borderWidth = topSection->outerBorderBefore(); 1095 if (borderWidth < 0) 1096 return 0; // Overridden by hidden 1097 } 1098 const BorderValue& tb = style()->borderBefore(); 1099 if (tb.style() == BHIDDEN) 1100 return 0; 1101 if (tb.style() > BHIDDEN) 1102 borderWidth = max<int>(borderWidth, tb.width() / 2); 1103 return borderWidth; 1104} 1105 1106int RenderTable::outerBorderAfter() const 1107{ 1108 if (!collapseBorders()) 1109 return 0; 1110 int borderWidth = 0; 1111 1112 if (RenderTableSection* section = bottomSection()) { 1113 borderWidth = section->outerBorderAfter(); 1114 if (borderWidth < 0) 1115 return 0; // Overridden by hidden 1116 } 1117 const BorderValue& tb = style()->borderAfter(); 1118 if (tb.style() == BHIDDEN) 1119 return 0; 1120 if (tb.style() > BHIDDEN) 1121 borderWidth = max<int>(borderWidth, (tb.width() + 1) / 2); 1122 return borderWidth; 1123} 1124 1125int RenderTable::outerBorderStart() const 1126{ 1127 if (!collapseBorders()) 1128 return 0; 1129 1130 int borderWidth = 0; 1131 1132 const BorderValue& tb = style()->borderStart(); 1133 if (tb.style() == BHIDDEN) 1134 return 0; 1135 if (tb.style() > BHIDDEN) 1136 borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 0 : 1)) / 2; 1137 1138 bool allHidden = true; 1139 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) { 1140 int sw = section->outerBorderStart(); 1141 if (sw < 0) 1142 continue; 1143 allHidden = false; 1144 borderWidth = max(borderWidth, sw); 1145 } 1146 if (allHidden) 1147 return 0; 1148 1149 return borderWidth; 1150} 1151 1152int RenderTable::outerBorderEnd() const 1153{ 1154 if (!collapseBorders()) 1155 return 0; 1156 1157 int borderWidth = 0; 1158 1159 const BorderValue& tb = style()->borderEnd(); 1160 if (tb.style() == BHIDDEN) 1161 return 0; 1162 if (tb.style() > BHIDDEN) 1163 borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 1 : 0)) / 2; 1164 1165 bool allHidden = true; 1166 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) { 1167 int sw = section->outerBorderEnd(); 1168 if (sw < 0) 1169 continue; 1170 allHidden = false; 1171 borderWidth = max(borderWidth, sw); 1172 } 1173 if (allHidden) 1174 return 0; 1175 1176 return borderWidth; 1177} 1178 1179RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const 1180{ 1181 recalcSectionsIfNeeded(); 1182 1183 if (section == m_head) 1184 return 0; 1185 1186 RenderObject* prevSection = section == m_foot ? lastChild() : section->previousSibling(); 1187 while (prevSection) { 1188 if (prevSection->isTableSection() && prevSection != m_head && prevSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || toRenderTableSection(prevSection)->numRows())) 1189 break; 1190 prevSection = prevSection->previousSibling(); 1191 } 1192 if (!prevSection && m_head && (skipEmptySections == DoNotSkipEmptySections || m_head->numRows())) 1193 prevSection = m_head; 1194 return toRenderTableSection(prevSection); 1195} 1196 1197RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const 1198{ 1199 recalcSectionsIfNeeded(); 1200 1201 if (section == m_foot) 1202 return 0; 1203 1204 RenderObject* nextSection = section == m_head ? firstChild() : section->nextSibling(); 1205 while (nextSection) { 1206 if (nextSection->isTableSection() && nextSection != m_head && nextSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || toRenderTableSection(nextSection)->numRows())) 1207 break; 1208 nextSection = nextSection->nextSibling(); 1209 } 1210 if (!nextSection && m_foot && (skipEmptySections == DoNotSkipEmptySections || m_foot->numRows())) 1211 nextSection = m_foot; 1212 return toRenderTableSection(nextSection); 1213} 1214 1215RenderTableSection* RenderTable::bottomSection() const 1216{ 1217 recalcSectionsIfNeeded(); 1218 1219 if (m_foot) 1220 return m_foot; 1221 1222 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { 1223 if (child->isTableSection()) 1224 return toRenderTableSection(child); 1225 } 1226 1227 return 0; 1228} 1229 1230RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const 1231{ 1232 recalcSectionsIfNeeded(); 1233 1234 // Find the section and row to look in 1235 unsigned r = cell->rowIndex(); 1236 RenderTableSection* section = 0; 1237 unsigned rAbove = 0; 1238 if (r > 0) { 1239 // cell is not in the first row, so use the above row in its own section 1240 section = cell->section(); 1241 rAbove = r - 1; 1242 } else { 1243 section = sectionAbove(cell->section(), SkipEmptySections); 1244 if (section) { 1245 ASSERT(section->numRows()); 1246 rAbove = section->numRows() - 1; 1247 } 1248 } 1249 1250 // Look up the cell in the section's grid, which requires effective col index 1251 if (section) { 1252 unsigned effCol = colToEffCol(cell->col()); 1253 RenderTableSection::CellStruct& aboveCell = section->cellAt(rAbove, effCol); 1254 return aboveCell.primaryCell(); 1255 } else 1256 return 0; 1257} 1258 1259RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const 1260{ 1261 recalcSectionsIfNeeded(); 1262 1263 // Find the section and row to look in 1264 unsigned r = cell->rowIndex() + cell->rowSpan() - 1; 1265 RenderTableSection* section = 0; 1266 unsigned rBelow = 0; 1267 if (r < cell->section()->numRows() - 1) { 1268 // The cell is not in the last row, so use the next row in the section. 1269 section = cell->section(); 1270 rBelow = r + 1; 1271 } else { 1272 section = sectionBelow(cell->section(), SkipEmptySections); 1273 if (section) 1274 rBelow = 0; 1275 } 1276 1277 // Look up the cell in the section's grid, which requires effective col index 1278 if (section) { 1279 unsigned effCol = colToEffCol(cell->col()); 1280 RenderTableSection::CellStruct& belowCell = section->cellAt(rBelow, effCol); 1281 return belowCell.primaryCell(); 1282 } else 1283 return 0; 1284} 1285 1286RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const 1287{ 1288 recalcSectionsIfNeeded(); 1289 1290 RenderTableSection* section = cell->section(); 1291 unsigned effCol = colToEffCol(cell->col()); 1292 if (!effCol) 1293 return 0; 1294 1295 // If we hit a colspan back up to a real cell. 1296 RenderTableSection::CellStruct& prevCell = section->cellAt(cell->rowIndex(), effCol - 1); 1297 return prevCell.primaryCell(); 1298} 1299 1300RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const 1301{ 1302 recalcSectionsIfNeeded(); 1303 1304 unsigned effCol = colToEffCol(cell->col() + cell->colSpan()); 1305 if (effCol >= numEffCols()) 1306 return 0; 1307 return cell->section()->primaryCellAt(cell->rowIndex(), effCol); 1308} 1309 1310RenderBlock* RenderTable::firstLineBlock() const 1311{ 1312 return 0; 1313} 1314 1315void RenderTable::updateFirstLetter() 1316{ 1317} 1318 1319int RenderTable::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const 1320{ 1321 LayoutUnit baseline = firstLineBoxBaseline(); 1322 if (baseline != -1) 1323 return baseline; 1324 1325 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); 1326} 1327 1328int RenderTable::inlineBlockBaseline(LineDirectionMode) const 1329{ 1330 // Tables are skipped when computing an inline-block's baseline. 1331 return -1; 1332} 1333 1334int RenderTable::firstLineBoxBaseline() const 1335{ 1336 // The baseline of a 'table' is the same as the 'inline-table' baseline per CSS 3 Flexbox (CSS 2.1 1337 // doesn't define the baseline of a 'table' only an 'inline-table'). 1338 // This is also needed to properly determine the baseline of a cell if it has a table child. 1339 1340 if (isWritingModeRoot()) 1341 return -1; 1342 1343 recalcSectionsIfNeeded(); 1344 1345 const RenderTableSection* topNonEmptySection = this->topNonEmptySection(); 1346 if (!topNonEmptySection) 1347 return -1; 1348 1349 int baseline = topNonEmptySection->firstLineBoxBaseline(); 1350 if (baseline > 0) 1351 return topNonEmptySection->logicalTop() + baseline; 1352 1353 // FIXME: A table row always has a baseline per CSS 2.1. Will this return the right value? 1354 return -1; 1355} 1356 1357LayoutRect RenderTable::overflowClipRect(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy) 1358{ 1359 LayoutRect rect = RenderBlock::overflowClipRect(location, region, relevancy); 1360 1361 // If we have a caption, expand the clip to include the caption. 1362 // FIXME: Technically this is wrong, but it's virtually impossible to fix this 1363 // for real until captions have been re-written. 1364 // FIXME: This code assumes (like all our other caption code) that only top/bottom are 1365 // supported. When we actually support left/right and stop mapping them to top/bottom, 1366 // we might have to hack this code first (depending on what order we do these bug fixes in). 1367 if (!m_captions.isEmpty()) { 1368 if (style()->isHorizontalWritingMode()) { 1369 rect.setHeight(height()); 1370 rect.setY(location.y()); 1371 } else { 1372 rect.setWidth(width()); 1373 rect.setX(location.x()); 1374 } 1375 } 1376 1377 return rect; 1378} 1379 1380bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) 1381{ 1382 LayoutPoint adjustedLocation = accumulatedOffset + location(); 1383 1384 // Check kids first. 1385 if (!hasOverflowClip() || locationInContainer.intersects(overflowClipRect(adjustedLocation, locationInContainer.region()))) { 1386 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { 1387 if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) { 1388 LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), adjustedLocation); 1389 if (child->nodeAtPoint(request, result, locationInContainer, childPoint, action)) { 1390 updateHitTestResult(result, toLayoutPoint(locationInContainer.point() - childPoint)); 1391 return true; 1392 } 1393 } 1394 } 1395 } 1396 1397 // Check our bounds next. 1398 LayoutRect boundsRect(adjustedLocation, size()); 1399 if (visibleToHitTesting() && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && locationInContainer.intersects(boundsRect)) { 1400 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(adjustedLocation))); 1401 if (!result.addNodeToRectBasedTestResult(node(), request, locationInContainer, boundsRect)) 1402 return true; 1403 } 1404 1405 return false; 1406} 1407 1408RenderTable* RenderTable::createAnonymousWithParentRenderer(const RenderObject* parent) 1409{ 1410 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE); 1411 RenderTable* newTable = new (parent->renderArena()) RenderTable(0); 1412 newTable->setDocumentForAnonymous(parent->document()); 1413 newTable->setStyle(newStyle.release()); 1414 return newTable; 1415} 1416 1417const BorderValue& RenderTable::tableStartBorderAdjoiningCell(const RenderTableCell* cell) const 1418{ 1419 ASSERT(cell->isFirstOrLastCellInRow()); 1420 if (hasSameDirectionAs(cell->row())) 1421 return style()->borderStart(); 1422 1423 return style()->borderEnd(); 1424} 1425 1426const BorderValue& RenderTable::tableEndBorderAdjoiningCell(const RenderTableCell* cell) const 1427{ 1428 ASSERT(cell->isFirstOrLastCellInRow()); 1429 if (hasSameDirectionAs(cell->row())) 1430 return style()->borderEnd(); 1431 1432 return style()->borderStart(); 1433} 1434 1435} 1436