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/frame/FrameView.h" 33 #include "core/rendering/AutoTableLayout.h" 34 #include "core/rendering/FixedTableLayout.h" 35 #include "core/rendering/GraphicsContextAnnotator.h" 36 #include "core/rendering/HitTestResult.h" 37 #include "core/rendering/LayoutRectRecorder.h" 38 #include "core/rendering/LayoutRepainter.h" 39 #include "core/rendering/RenderLayer.h" 40 #include "core/rendering/RenderTableCaption.h" 41 #include "core/rendering/RenderTableCell.h" 42 #include "core/rendering/RenderTableCol.h" 43 #include "core/rendering/RenderTableSection.h" 44 #include "core/rendering/RenderView.h" 45 #include "core/rendering/SubtreeLayoutScope.h" 46 #include "core/rendering/style/StyleInheritedData.h" 47 #include "platform/graphics/GraphicsContextStateSaver.h" 48 49 using namespace std; 50 51 namespace WebCore { 52 53 using namespace HTMLNames; 54 55 RenderTable::RenderTable(Element* element) 56 : RenderBlock(element) 57 , m_head(0) 58 , m_foot(0) 59 , m_firstBody(0) 60 , m_currentBorder(0) 61 , m_collapsedBordersValid(false) 62 , m_hasColElements(false) 63 , m_needsSectionRecalc(false) 64 , m_columnLogicalWidthChanged(false) 65 , m_columnRenderersValid(false) 66 , m_hSpacing(0) 67 , m_vSpacing(0) 68 , m_borderStart(0) 69 , m_borderEnd(0) 70 { 71 setChildrenInline(false); 72 m_columnPos.fill(0, 1); 73 74 } 75 76 RenderTable::~RenderTable() 77 { 78 } 79 80 void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 81 { 82 RenderBlock::styleDidChange(diff, oldStyle); 83 propagateStyleToAnonymousChildren(); 84 85 ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO; 86 87 // In the collapsed border model, there is no cell spacing. 88 m_hSpacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing(); 89 m_vSpacing = collapseBorders() ? 0 : style()->verticalBorderSpacing(); 90 m_columnPos[0] = m_hSpacing; 91 92 if (!m_tableLayout || style()->tableLayout() != oldTableLayout) { 93 if (m_tableLayout) 94 m_tableLayout->willChangeTableLayout(); 95 96 // According to the CSS2 spec, you only use fixed table layout if an 97 // explicit width is specified on the table. Auto width implies auto table layout. 98 if (style()->tableLayout() == TFIXED && !style()->logicalWidth().isAuto()) 99 m_tableLayout = adoptPtr(new FixedTableLayout(this)); 100 else 101 m_tableLayout = adoptPtr(new AutoTableLayout(this)); 102 } 103 104 // If border was changed, invalidate collapsed borders cache. 105 if (!needsLayout() && oldStyle && oldStyle->border() != style()->border()) 106 invalidateCollapsedBorders(); 107 } 108 109 static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before) 110 { 111 if (!before || !ptr) 112 return; 113 RenderObject* o = before->previousSibling(); 114 while (o && o != ptr) 115 o = o->previousSibling(); 116 if (!o) 117 ptr = 0; 118 } 119 120 void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) 121 { 122 bool wrapInAnonymousSection = !child->isOutOfFlowPositioned(); 123 124 if (child->isTableCaption()) 125 wrapInAnonymousSection = false; 126 else if (child->isRenderTableCol()) { 127 m_hasColElements = true; 128 wrapInAnonymousSection = false; 129 } else if (child->isTableSection()) { 130 switch (child->style()->display()) { 131 case TABLE_HEADER_GROUP: 132 resetSectionPointerIfNotBefore(m_head, beforeChild); 133 if (!m_head) { 134 m_head = toRenderTableSection(child); 135 } else { 136 resetSectionPointerIfNotBefore(m_firstBody, beforeChild); 137 if (!m_firstBody) 138 m_firstBody = toRenderTableSection(child); 139 } 140 wrapInAnonymousSection = false; 141 break; 142 case TABLE_FOOTER_GROUP: 143 resetSectionPointerIfNotBefore(m_foot, beforeChild); 144 if (!m_foot) { 145 m_foot = toRenderTableSection(child); 146 wrapInAnonymousSection = false; 147 break; 148 } 149 // Fall through. 150 case TABLE_ROW_GROUP: 151 resetSectionPointerIfNotBefore(m_firstBody, beforeChild); 152 if (!m_firstBody) 153 m_firstBody = toRenderTableSection(child); 154 wrapInAnonymousSection = false; 155 break; 156 default: 157 ASSERT_NOT_REACHED(); 158 } 159 } else if (child->isTableCell() || child->isTableRow()) 160 wrapInAnonymousSection = true; 161 else 162 wrapInAnonymousSection = true; 163 164 if (child->isTableSection()) 165 setNeedsSectionRecalc(); 166 167 if (!wrapInAnonymousSection) { 168 if (beforeChild && beforeChild->parent() != this) 169 beforeChild = splitAnonymousBoxesAroundChild(beforeChild); 170 171 RenderBox::addChild(child, beforeChild); 172 return; 173 } 174 175 if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous() && !lastChild()->isBeforeContent()) { 176 lastChild()->addChild(child); 177 return; 178 } 179 180 if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) { 181 RenderObject* section = beforeChild->previousSibling(); 182 if (section && section->isTableSection() && section->isAnonymous()) { 183 section->addChild(child); 184 return; 185 } 186 } 187 188 RenderObject* lastBox = beforeChild; 189 while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION && lastBox->style()->display() != TABLE_COLUMN_GROUP) 190 lastBox = lastBox->parent(); 191 if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox)) { 192 if (beforeChild == lastBox) 193 beforeChild = lastBox->firstChild(); 194 lastBox->addChild(child, beforeChild); 195 return; 196 } 197 198 if (beforeChild && !beforeChild->isTableSection() && beforeChild->style()->display() != TABLE_CAPTION && beforeChild->style()->display() != TABLE_COLUMN_GROUP) 199 beforeChild = 0; 200 201 RenderTableSection* section = RenderTableSection::createAnonymousWithParentRenderer(this); 202 addChild(section, beforeChild); 203 section->addChild(child); 204 } 205 206 void RenderTable::addCaption(const RenderTableCaption* caption) 207 { 208 ASSERT(m_captions.find(caption) == kNotFound); 209 m_captions.append(const_cast<RenderTableCaption*>(caption)); 210 } 211 212 void RenderTable::removeCaption(const RenderTableCaption* oldCaption) 213 { 214 size_t index = m_captions.find(oldCaption); 215 ASSERT(index != kNotFound); 216 if (index == kNotFound) 217 return; 218 219 m_captions.remove(index); 220 } 221 222 void RenderTable::invalidateCachedColumns() 223 { 224 m_columnRenderersValid = false; 225 m_columnRenderers.resize(0); 226 } 227 228 void RenderTable::addColumn(const RenderTableCol*) 229 { 230 invalidateCachedColumns(); 231 } 232 233 void RenderTable::removeColumn(const RenderTableCol*) 234 { 235 invalidateCachedColumns(); 236 // We don't really need to recompute our sections, but we need to update our 237 // column count and whether we have a column. Currently, we only have one 238 // size-fit-all flag but we may have to consider splitting it. 239 setNeedsSectionRecalc(); 240 } 241 242 void RenderTable::updateLogicalWidth() 243 { 244 recalcSectionsIfNeeded(); 245 246 if (isOutOfFlowPositioned()) { 247 LogicalExtentComputedValues computedValues; 248 computePositionedLogicalWidth(computedValues); 249 setLogicalWidth(computedValues.m_extent); 250 setLogicalLeft(computedValues.m_position); 251 setMarginStart(computedValues.m_margins.m_start); 252 setMarginEnd(computedValues.m_margins.m_end); 253 } 254 255 RenderBlock* cb = containingBlock(); 256 RenderView* renderView = view(); 257 258 LayoutUnit availableLogicalWidth = containingBlockLogicalWidthForContent(); 259 bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode(); 260 LayoutUnit containerWidthInInlineDirection = hasPerpendicularContainingBlock ? perpendicularContainingBlockLogicalHeight() : availableLogicalWidth; 261 262 Length styleLogicalWidth = style()->logicalWidth(); 263 if ((styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive()) || styleLogicalWidth.isIntrinsic()) 264 setLogicalWidth(convertStyleLogicalWidthToComputedWidth(styleLogicalWidth, containerWidthInInlineDirection)); 265 else { 266 // Subtract out any fixed margins from our available width for auto width tables. 267 LayoutUnit marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth, renderView); 268 LayoutUnit marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth, renderView); 269 LayoutUnit marginTotal = marginStart + marginEnd; 270 271 // Subtract out our margins to get the available content width. 272 LayoutUnit availableContentLogicalWidth = max<LayoutUnit>(0, containerWidthInInlineDirection - marginTotal); 273 if (shrinkToAvoidFloats() && cb->containsFloats() && !hasPerpendicularContainingBlock) { 274 // FIXME: Work with regions someday. 275 availableContentLogicalWidth = shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, toRenderBlockFlow(cb), 0); 276 } 277 278 // Ensure we aren't bigger than our available width. 279 setLogicalWidth(min<int>(availableContentLogicalWidth, maxPreferredLogicalWidth())); 280 } 281 282 // Ensure we aren't bigger than our max-width style. 283 Length styleMaxLogicalWidth = style()->logicalMaxWidth(); 284 if ((styleMaxLogicalWidth.isSpecified() && !styleMaxLogicalWidth.isNegative()) || styleMaxLogicalWidth.isIntrinsic()) { 285 LayoutUnit computedMaxLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMaxLogicalWidth, availableLogicalWidth); 286 setLogicalWidth(min<int>(logicalWidth(), computedMaxLogicalWidth)); 287 } 288 289 // Ensure we aren't smaller than our min preferred width. This MUST be done after 'max-width' as 290 // we ignore it if it means we wouldn't accomodate our content. 291 setLogicalWidth(max<int>(logicalWidth(), minPreferredLogicalWidth())); 292 293 // Ensure we aren't smaller than our min-width style. 294 Length styleMinLogicalWidth = style()->logicalMinWidth(); 295 if ((styleMinLogicalWidth.isSpecified() && !styleMinLogicalWidth.isNegative()) || styleMinLogicalWidth.isIntrinsic()) { 296 LayoutUnit computedMinLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMinLogicalWidth, availableLogicalWidth); 297 setLogicalWidth(max<int>(logicalWidth(), computedMinLogicalWidth)); 298 } 299 300 // Finally, with our true width determined, compute our margins for real. 301 setMarginStart(0); 302 setMarginEnd(0); 303 if (!hasPerpendicularContainingBlock) { 304 LayoutUnit containerLogicalWidthForAutoMargins = availableLogicalWidth; 305 if (avoidsFloats() && cb->containsFloats()) 306 containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidthInRegion(0); // FIXME: Work with regions someday. 307 ComputedMarginValues marginValues; 308 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() == style()->isLeftToRightDirection(); 309 computeInlineDirectionMargins(cb, containerLogicalWidthForAutoMargins, logicalWidth(), 310 hasInvertedDirection ? marginValues.m_start : marginValues.m_end, 311 hasInvertedDirection ? marginValues.m_end : marginValues.m_start); 312 setMarginStart(marginValues.m_start); 313 setMarginEnd(marginValues.m_end); 314 } else { 315 setMarginStart(minimumValueForLength(style()->marginStart(), availableLogicalWidth, renderView)); 316 setMarginEnd(minimumValueForLength(style()->marginEnd(), availableLogicalWidth, renderView)); 317 } 318 319 // We should NEVER shrink the table below the min-content logical width, or else the table can't accomodate 320 // its own content which doesn't match CSS nor what authors expect. 321 // FIXME: When we convert to sub-pixel layout for tables we can remove the int conversion 322 // https://code.google.com/p/chromium/issues/detail?id=241198 323 ASSERT(logicalWidth().toInt() >= minPreferredLogicalWidth().toInt()); 324 } 325 326 // This method takes a RenderStyle's logical width, min-width, or max-width length and computes its actual value. 327 LayoutUnit RenderTable::convertStyleLogicalWidthToComputedWidth(const Length& styleLogicalWidth, LayoutUnit availableWidth) 328 { 329 if (styleLogicalWidth.isIntrinsic()) 330 return computeIntrinsicLogicalWidthUsing(styleLogicalWidth, availableWidth, bordersPaddingAndSpacingInRowDirection()); 331 332 // HTML tables' width styles already include borders and paddings, but CSS tables' width styles do not. 333 LayoutUnit borders = 0; 334 bool isCSSTable = !node() || !isHTMLTableElement(node()); 335 if (isCSSTable && styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive() && style()->boxSizing() == CONTENT_BOX) 336 borders = borderStart() + borderEnd() + (collapseBorders() ? LayoutUnit() : paddingStart() + paddingEnd()); 337 338 return minimumValueForLength(styleLogicalWidth, availableWidth, view()) + borders; 339 } 340 341 LayoutUnit RenderTable::convertStyleLogicalHeightToComputedHeight(const Length& styleLogicalHeight) 342 { 343 LayoutUnit borderAndPaddingBefore = borderBefore() + (collapseBorders() ? LayoutUnit() : paddingBefore()); 344 LayoutUnit borderAndPaddingAfter = borderAfter() + (collapseBorders() ? LayoutUnit() : paddingAfter()); 345 LayoutUnit borderAndPadding = borderAndPaddingBefore + borderAndPaddingAfter; 346 LayoutUnit computedLogicalHeight = 0; 347 if (styleLogicalHeight.isFixed()) { 348 // HTML tables size as though CSS height includes border/padding, CSS tables do not. 349 LayoutUnit borders = LayoutUnit(); 350 // FIXME: We cannot apply box-sizing: content-box on <table> which other browsers allow. 351 if ((node() && isHTMLTableElement(node())) || style()->boxSizing() == BORDER_BOX) { 352 borders = borderAndPadding; 353 } 354 computedLogicalHeight = styleLogicalHeight.value() - borders; 355 } else if (styleLogicalHeight.isPercent()) 356 computedLogicalHeight = computePercentageLogicalHeight(styleLogicalHeight); 357 else if (styleLogicalHeight.isViewportPercentage()) 358 computedLogicalHeight = minimumValueForLength(styleLogicalHeight, 0, view()); 359 else if (styleLogicalHeight.isIntrinsic()) 360 computedLogicalHeight = computeIntrinsicLogicalContentHeightUsing(styleLogicalHeight, logicalHeight() - borderAndPadding, borderAndPadding); 361 else 362 ASSERT_NOT_REACHED(); 363 return max<LayoutUnit>(0, computedLogicalHeight); 364 } 365 366 void RenderTable::layoutCaption(RenderTableCaption* caption) 367 { 368 LayoutRect captionRect(caption->frameRect()); 369 370 if (caption->needsLayout()) { 371 // The margins may not be available but ensure the caption is at least located beneath any previous sibling caption 372 // so that it does not mistakenly think any floats in the previous caption intrude into it. 373 caption->setLogicalLocation(LayoutPoint(caption->marginStart(), collapsedMarginBeforeForChild(caption) + logicalHeight())); 374 // If RenderTableCaption ever gets a layout() function, use it here. 375 caption->layoutIfNeeded(); 376 } 377 // Apply the margins to the location now that they are definitely available from layout 378 caption->setLogicalLocation(LayoutPoint(caption->marginStart(), collapsedMarginBeforeForChild(caption) + logicalHeight())); 379 380 if (!selfNeedsLayout() && caption->checkForRepaintDuringLayout()) 381 caption->repaintDuringLayoutIfMoved(captionRect); 382 383 setLogicalHeight(logicalHeight() + caption->logicalHeight() + collapsedMarginBeforeForChild(caption) + collapsedMarginAfterForChild(caption)); 384 } 385 386 void RenderTable::distributeExtraLogicalHeight(int extraLogicalHeight) 387 { 388 if (extraLogicalHeight <= 0) 389 return; 390 391 // FIXME: Distribute the extra logical height between all table sections instead of giving it all to the first one. 392 if (RenderTableSection* section = firstBody()) 393 extraLogicalHeight -= section->distributeExtraLogicalHeightToRows(extraLogicalHeight); 394 395 // FIXME: We really would like to enable this ASSERT to ensure that all the extra space has been distributed. 396 // However our current distribution algorithm does not round properly and thus we can have some remaining height. 397 // ASSERT(!topSection() || !extraLogicalHeight); 398 } 399 400 void RenderTable::simplifiedNormalFlowLayout() 401 { 402 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) { 403 section->layoutIfNeeded(); 404 section->computeOverflowFromCells(); 405 } 406 } 407 408 void RenderTable::layout() 409 { 410 ASSERT(needsLayout()); 411 412 LayoutRectRecorder recorder(*this); 413 414 if (simplifiedLayout()) 415 return; 416 417 recalcSectionsIfNeeded(); 418 // FIXME: We should do this recalc lazily in borderStart/borderEnd so that we don't have to make sure 419 // to call this before we call borderStart/borderEnd to avoid getting a stale value. 420 recalcBordersInRowDirection(); 421 422 LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); 423 LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode()); 424 425 setLogicalHeight(0); 426 427 LayoutUnit oldLogicalWidth = logicalWidth(); 428 updateLogicalWidth(); 429 430 SubtreeLayoutScope layouter(this); 431 432 if (logicalWidth() != oldLogicalWidth) { 433 for (unsigned i = 0; i < m_captions.size(); i++) 434 layouter.setNeedsLayout(m_captions[i]); 435 } 436 // FIXME: The optimisation below doesn't work since the internal table 437 // layout could have changed. we need to add a flag to the table 438 // layout that tells us if something has changed in the min max 439 // calculations to do it correctly. 440 // if ( oldWidth != width() || columns.size() + 1 != columnPos.size() ) 441 m_tableLayout->layout(); 442 443 LayoutUnit totalSectionLogicalHeight = 0; 444 LayoutUnit oldTableLogicalTop = 0; 445 for (unsigned i = 0; i < m_captions.size(); i++) 446 oldTableLogicalTop += m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter(); 447 448 bool collapsing = collapseBorders(); 449 450 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 451 if (child->isTableSection()) { 452 RenderTableSection* section = toRenderTableSection(child); 453 if (m_columnLogicalWidthChanged) 454 layouter.setChildNeedsLayout(section); 455 section->layoutIfNeeded(); 456 totalSectionLogicalHeight += section->calcRowLogicalHeight(); 457 if (collapsing) 458 section->recalcOuterBorder(); 459 ASSERT(!section->needsLayout()); 460 } else if (child->isRenderTableCol()) { 461 child->layoutIfNeeded(); 462 ASSERT(!child->needsLayout()); 463 } else { 464 // FIXME: We should never have other type of children (they should be wrapped in an 465 // anonymous table section) but our code is too crazy and this can happen in practice. 466 // Until this is fixed, let's make sure we don't leave non laid out children in the tree. 467 child->layoutIfNeeded(); 468 } 469 } 470 471 // If any table section moved vertically, we will just repaint everything from that 472 // section down (it is quite unlikely that any of the following sections 473 // did not shift). 474 bool sectionMoved = false; 475 LayoutUnit movedSectionLogicalTop = 0; 476 477 // FIXME: Collapse caption margin. 478 if (!m_captions.isEmpty()) { 479 for (unsigned i = 0; i < m_captions.size(); i++) { 480 if (m_captions[i]->style()->captionSide() == CAPBOTTOM) 481 continue; 482 layoutCaption(m_captions[i]); 483 } 484 if (logicalHeight() != oldTableLogicalTop) { 485 sectionMoved = true; 486 movedSectionLogicalTop = min(logicalHeight(), oldTableLogicalTop); 487 } 488 } 489 490 LayoutUnit borderAndPaddingBefore = borderBefore() + (collapsing ? LayoutUnit() : paddingBefore()); 491 LayoutUnit borderAndPaddingAfter = borderAfter() + (collapsing ? LayoutUnit() : paddingAfter()); 492 493 setLogicalHeight(logicalHeight() + borderAndPaddingBefore); 494 495 if (!isOutOfFlowPositioned()) 496 updateLogicalHeight(); 497 498 LayoutUnit computedLogicalHeight = 0; 499 500 Length logicalHeightLength = style()->logicalHeight(); 501 if (logicalHeightLength.isIntrinsic() || (logicalHeightLength.isSpecified() && logicalHeightLength.isPositive())) 502 computedLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalHeightLength); 503 504 Length logicalMaxHeightLength = style()->logicalMaxHeight(); 505 if (logicalMaxHeightLength.isIntrinsic() || (logicalMaxHeightLength.isSpecified() && !logicalMaxHeightLength.isNegative())) { 506 LayoutUnit computedMaxLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMaxHeightLength); 507 computedLogicalHeight = min(computedLogicalHeight, computedMaxLogicalHeight); 508 } 509 510 Length logicalMinHeightLength = style()->logicalMinHeight(); 511 if (logicalMinHeightLength.isIntrinsic() || (logicalMinHeightLength.isSpecified() && !logicalMinHeightLength.isNegative())) { 512 LayoutUnit computedMinLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMinHeightLength); 513 computedLogicalHeight = max(computedLogicalHeight, computedMinLogicalHeight); 514 } 515 516 distributeExtraLogicalHeight(floorToInt(computedLogicalHeight - totalSectionLogicalHeight)); 517 518 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) 519 section->layoutRows(); 520 521 if (!topSection() && computedLogicalHeight > totalSectionLogicalHeight && !document().inQuirksMode()) { 522 // Completely empty tables (with no sections or anything) should at least honor specified height 523 // in strict mode. 524 setLogicalHeight(logicalHeight() + computedLogicalHeight); 525 } 526 527 LayoutUnit sectionLogicalLeft = style()->isLeftToRightDirection() ? borderStart() : borderEnd(); 528 if (!collapsing) 529 sectionLogicalLeft += style()->isLeftToRightDirection() ? paddingStart() : paddingEnd(); 530 531 // position the table sections 532 RenderTableSection* section = topSection(); 533 while (section) { 534 if (!sectionMoved && section->logicalTop() != logicalHeight()) { 535 sectionMoved = true; 536 movedSectionLogicalTop = min(logicalHeight(), section->logicalTop()) + (style()->isHorizontalWritingMode() ? section->visualOverflowRect().y() : section->visualOverflowRect().x()); 537 } 538 section->setLogicalLocation(LayoutPoint(sectionLogicalLeft, logicalHeight())); 539 540 setLogicalHeight(logicalHeight() + section->logicalHeight()); 541 section = sectionBelow(section); 542 } 543 544 setLogicalHeight(logicalHeight() + borderAndPaddingAfter); 545 546 for (unsigned i = 0; i < m_captions.size(); i++) { 547 if (m_captions[i]->style()->captionSide() != CAPBOTTOM) 548 continue; 549 layoutCaption(m_captions[i]); 550 } 551 552 if (isOutOfFlowPositioned()) 553 updateLogicalHeight(); 554 555 // table can be containing block of positioned elements. 556 // FIXME: Only pass true if width or height changed. 557 layoutPositionedObjects(true); 558 559 updateLayerTransform(); 560 561 // Layout was changed, so probably borders too. 562 invalidateCollapsedBorders(); 563 564 computeOverflow(clientLogicalBottom()); 565 566 statePusher.pop(); 567 568 if (view()->layoutState()->pageLogicalHeight()) 569 setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(this, logicalTop())); 570 571 bool didFullRepaint = repainter.repaintAfterLayout(); 572 // Repaint with our new bounds if they are different from our old bounds. 573 if (!didFullRepaint && sectionMoved) { 574 if (style()->isHorizontalWritingMode()) 575 repaintRectangle(LayoutRect(visualOverflowRect().x(), movedSectionLogicalTop, visualOverflowRect().width(), visualOverflowRect().maxY() - movedSectionLogicalTop)); 576 else 577 repaintRectangle(LayoutRect(movedSectionLogicalTop, visualOverflowRect().y(), visualOverflowRect().maxX() - movedSectionLogicalTop, visualOverflowRect().height())); 578 } 579 580 m_columnLogicalWidthChanged = false; 581 clearNeedsLayout(); 582 } 583 584 // Collect all the unique border values that we want to paint in a sorted list. 585 void RenderTable::recalcCollapsedBorders() 586 { 587 if (m_collapsedBordersValid) 588 return; 589 m_collapsedBordersValid = true; 590 m_collapsedBorders.clear(); 591 for (RenderObject* section = firstChild(); section; section = section->nextSibling()) { 592 if (!section->isTableSection()) 593 continue; 594 for (RenderObject* row = section->firstChild(); row; row = row->nextSibling()) { 595 if (!row->isTableRow()) 596 continue; 597 for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { 598 if (!cell->isTableCell()) 599 continue; 600 ASSERT(toRenderTableCell(cell)->table() == this); 601 toRenderTableCell(cell)->collectBorderValues(m_collapsedBorders); 602 } 603 } 604 } 605 RenderTableCell::sortBorderValues(m_collapsedBorders); 606 } 607 608 609 void RenderTable::addOverflowFromChildren() 610 { 611 // Add overflow from borders. 612 // Technically it's odd that we are incorporating the borders into layout overflow, which is only supposed to be about overflow from our 613 // descendant objects, but since tables don't support overflow:auto, this works out fine. 614 if (collapseBorders()) { 615 int rightBorderOverflow = width() + outerBorderRight() - borderRight(); 616 int leftBorderOverflow = borderLeft() - outerBorderLeft(); 617 int bottomBorderOverflow = height() + outerBorderBottom() - borderBottom(); 618 int topBorderOverflow = borderTop() - outerBorderTop(); 619 IntRect borderOverflowRect(leftBorderOverflow, topBorderOverflow, rightBorderOverflow - leftBorderOverflow, bottomBorderOverflow - topBorderOverflow); 620 if (borderOverflowRect != pixelSnappedBorderBoxRect()) { 621 addLayoutOverflow(borderOverflowRect); 622 addVisualOverflow(borderOverflowRect); 623 } 624 } 625 626 // Add overflow from our caption. 627 for (unsigned i = 0; i < m_captions.size(); i++) 628 addOverflowFromChild(m_captions[i]); 629 630 // Add overflow from our sections. 631 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) 632 addOverflowFromChild(section); 633 } 634 635 void RenderTable::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 636 { 637 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this); 638 639 LayoutPoint adjustedPaintOffset = paintOffset + location(); 640 641 PaintPhase paintPhase = paintInfo.phase; 642 643 if (!isRoot()) { 644 LayoutRect overflowBox = visualOverflowRect(); 645 flipForWritingMode(overflowBox); 646 overflowBox.inflate(maximalOutlineSize(paintInfo.phase)); 647 overflowBox.moveBy(adjustedPaintOffset); 648 if (!overflowBox.intersects(paintInfo.rect)) 649 return; 650 } 651 652 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, ForceContentsClip); 653 paintObject(paintInfo, adjustedPaintOffset); 654 if (pushedClip) 655 popContentsClip(paintInfo, paintPhase, adjustedPaintOffset); 656 } 657 658 void RenderTable::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 659 { 660 PaintPhase paintPhase = paintInfo.phase; 661 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE) 662 paintBoxDecorations(paintInfo, paintOffset); 663 664 if (paintPhase == PaintPhaseMask) { 665 paintMask(paintInfo, paintOffset); 666 return; 667 } 668 669 // We're done. We don't bother painting any children. 670 if (paintPhase == PaintPhaseBlockBackground) 671 return; 672 673 // We don't paint our own background, but we do let the kids paint their backgrounds. 674 if (paintPhase == PaintPhaseChildBlockBackgrounds) 675 paintPhase = PaintPhaseChildBlockBackground; 676 677 PaintInfo info(paintInfo); 678 info.phase = paintPhase; 679 info.updatePaintingRootForChildren(this); 680 681 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 682 if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) { 683 LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), paintOffset); 684 child->paint(info, childPoint); 685 } 686 } 687 688 if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) { 689 recalcCollapsedBorders(); 690 // Using our cached sorted styles, we then do individual passes, 691 // painting each style of border from lowest precedence to highest precedence. 692 info.phase = PaintPhaseCollapsedTableBorders; 693 size_t count = m_collapsedBorders.size(); 694 for (size_t i = 0; i < count; ++i) { 695 m_currentBorder = &m_collapsedBorders[i]; 696 for (RenderTableSection* section = bottomSection(); section; section = sectionAbove(section)) { 697 LayoutPoint childPoint = flipForWritingModeForChild(section, paintOffset); 698 section->paint(info, childPoint); 699 } 700 } 701 m_currentBorder = 0; 702 } 703 704 // Paint outline. 705 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE) 706 paintOutline(paintInfo, LayoutRect(paintOffset, size())); 707 } 708 709 void RenderTable::subtractCaptionRect(LayoutRect& rect) const 710 { 711 for (unsigned i = 0; i < m_captions.size(); i++) { 712 LayoutUnit captionLogicalHeight = m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter(); 713 bool captionIsBefore = (m_captions[i]->style()->captionSide() != CAPBOTTOM) ^ style()->isFlippedBlocksWritingMode(); 714 if (style()->isHorizontalWritingMode()) { 715 rect.setHeight(rect.height() - captionLogicalHeight); 716 if (captionIsBefore) 717 rect.move(0, captionLogicalHeight); 718 } else { 719 rect.setWidth(rect.width() - captionLogicalHeight); 720 if (captionIsBefore) 721 rect.move(captionLogicalHeight, 0); 722 } 723 } 724 } 725 726 void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 727 { 728 if (!paintInfo.shouldPaintWithinRoot(this)) 729 return; 730 731 LayoutRect rect(paintOffset, size()); 732 subtractCaptionRect(rect); 733 paintBoxDecorationsWithRect(paintInfo, paintOffset, rect); 734 } 735 736 void RenderTable::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 737 { 738 if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) 739 return; 740 741 LayoutRect rect(paintOffset, size()); 742 subtractCaptionRect(rect); 743 744 paintMaskImages(paintInfo, rect); 745 } 746 747 void RenderTable::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) const 748 { 749 recalcSectionsIfNeeded(); 750 // FIXME: Do the recalc in borderStart/borderEnd and make those const_cast this call. 751 // Then m_borderStart/m_borderEnd will be transparent a cache and it removes the possibility 752 // of reading out stale values. 753 const_cast<RenderTable*>(this)->recalcBordersInRowDirection(); 754 // FIXME: Restructure the table layout code so that we can make this method const. 755 const_cast<RenderTable*>(this)->m_tableLayout->computeIntrinsicLogicalWidths(minWidth, maxWidth); 756 757 // FIXME: We should include captions widths here like we do in computePreferredLogicalWidths. 758 } 759 760 void RenderTable::computePreferredLogicalWidths() 761 { 762 ASSERT(preferredLogicalWidthsDirty()); 763 764 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); 765 766 int bordersPaddingAndSpacing = bordersPaddingAndSpacingInRowDirection(); 767 m_minPreferredLogicalWidth += bordersPaddingAndSpacing; 768 m_maxPreferredLogicalWidth += bordersPaddingAndSpacing; 769 770 m_tableLayout->applyPreferredLogicalWidthQuirks(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); 771 772 for (unsigned i = 0; i < m_captions.size(); i++) 773 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_captions[i]->minPreferredLogicalWidth()); 774 775 RenderStyle* styleToUse = style(); 776 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for min-width. 777 if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) { 778 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); 779 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); 780 } 781 782 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for maxWidth. 783 if (styleToUse->logicalMaxWidth().isFixed()) { 784 // We don't constrain m_minPreferredLogicalWidth as the table should be at least the size of its min-content, regardless of 'max-width'. 785 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); 786 m_maxPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); 787 } 788 789 // FIXME: We should be adding borderAndPaddingLogicalWidth here, but m_tableLayout->computePreferredLogicalWidths already does, 790 // so a bunch of tests break doing this naively. 791 clearPreferredLogicalWidthsDirty(); 792 } 793 794 RenderTableSection* RenderTable::topNonEmptySection() const 795 { 796 RenderTableSection* section = topSection(); 797 if (section && !section->numRows()) 798 section = sectionBelow(section, SkipEmptySections); 799 return section; 800 } 801 802 void RenderTable::splitColumn(unsigned position, unsigned firstSpan) 803 { 804 // We split the column at "position", taking "firstSpan" cells from the span. 805 ASSERT(m_columns[position].span > firstSpan); 806 m_columns.insert(position, ColumnStruct(firstSpan)); 807 m_columns[position + 1].span -= firstSpan; 808 809 // Propagate the change in our columns representation to the sections that don't need 810 // cell recalc. If they do, they will be synced up directly with m_columns later. 811 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 812 if (!child->isTableSection()) 813 continue; 814 815 RenderTableSection* section = toRenderTableSection(child); 816 if (section->needsCellRecalc()) 817 continue; 818 819 section->splitColumn(position, firstSpan); 820 } 821 822 m_columnPos.grow(numEffCols() + 1); 823 } 824 825 void RenderTable::appendColumn(unsigned span) 826 { 827 unsigned newColumnIndex = m_columns.size(); 828 m_columns.append(ColumnStruct(span)); 829 830 // Propagate the change in our columns representation to the sections that don't need 831 // cell recalc. If they do, they will be synced up directly with m_columns later. 832 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 833 if (!child->isTableSection()) 834 continue; 835 836 RenderTableSection* section = toRenderTableSection(child); 837 if (section->needsCellRecalc()) 838 continue; 839 840 section->appendColumn(newColumnIndex); 841 } 842 843 m_columnPos.grow(numEffCols() + 1); 844 } 845 846 RenderTableCol* RenderTable::firstColumn() const 847 { 848 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 849 if (child->isRenderTableCol()) 850 return toRenderTableCol(child); 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