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