2 * Copyright (C) 1997 Martin Jones (mjones@kde.org)
3 * (C) 1997 Torben Weis (weis@kde.org)
4 * (C) 1998 Waldo Bastian (bastian@kde.org)
5 * (C) 1999 Lars Knoll (knoll@kde.org)
6 * (C) 1999 Antti Koivisto (koivisto@kde.org)
7 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
8 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
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.
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.
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.
27 #include "core/rendering/RenderTable.h"
29 #include "HTMLNames.h"
30 #include "core/dom/Document.h"
31 #include "core/frame/FrameView.h"
32 #include "core/rendering/AutoTableLayout.h"
33 #include "core/rendering/FixedTableLayout.h"
34 #include "core/rendering/GraphicsContextAnnotator.h"
35 #include "core/rendering/HitTestResult.h"
36 #include "core/rendering/LayoutRectRecorder.h"
37 #include "core/rendering/LayoutRepainter.h"
38 #include "core/rendering/RenderLayer.h"
39 #include "core/rendering/RenderTableCaption.h"
40 #include "core/rendering/RenderTableCell.h"
41 #include "core/rendering/RenderTableCol.h"
42 #include "core/rendering/RenderTableSection.h"
43 #include "core/rendering/RenderView.h"
44 #include "core/rendering/SubtreeLayoutScope.h"
45 #include "core/rendering/style/StyleInheritedData.h"
46 #include "platform/graphics/GraphicsContextStateSaver.h"
52 using namespace HTMLNames;
54 RenderTable::RenderTable(Element* element)
55 : RenderBlock(element)
60 , m_collapsedBordersValid(false)
61 , m_hasColElements(false)
62 , m_needsSectionRecalc(false)
63 , m_columnLogicalWidthChanged(false)
64 , m_columnRenderersValid(false)
65 , m_hasCellColspanThatDeterminesTableWidth(false)
71 setChildrenInline(false);
72 m_columnPos.fill(0, 1);
76 RenderTable::~RenderTable()
80 void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
82 RenderBlock::styleDidChange(diff, oldStyle);
83 propagateStyleToAnonymousChildren();
85 ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO;
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;
92 if (!m_tableLayout || style()->tableLayout() != oldTableLayout) {
94 m_tableLayout->willChangeTableLayout();
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));
101 m_tableLayout = adoptPtr(new AutoTableLayout(this));
104 // If border was changed, invalidate collapsed borders cache.
105 if (!needsLayout() && oldStyle && oldStyle->border() != style()->border())
106 invalidateCollapsedBorders();
109 static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before)
113 RenderObject* o = before->previousSibling();
114 while (o && o != ptr)
115 o = o->previousSibling();
120 static inline bool needsTableSection(RenderObject* object)
122 // Return true if 'object' can't exist in an anonymous table without being
123 // wrapped in a table section box.
124 EDisplay display = object->style()->display();
125 return display != TABLE_CAPTION && display != TABLE_COLUMN_GROUP && display != TABLE_COLUMN;
128 void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild)
130 bool wrapInAnonymousSection = !child->isOutOfFlowPositioned();
132 if (child->isTableCaption())
133 wrapInAnonymousSection = false;
134 else if (child->isRenderTableCol()) {
135 m_hasColElements = true;
136 wrapInAnonymousSection = false;
137 } else if (child->isTableSection()) {
138 switch (child->style()->display()) {
139 case TABLE_HEADER_GROUP:
140 resetSectionPointerIfNotBefore(m_head, beforeChild);
142 m_head = toRenderTableSection(child);
144 resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
146 m_firstBody = toRenderTableSection(child);
148 wrapInAnonymousSection = false;
150 case TABLE_FOOTER_GROUP:
151 resetSectionPointerIfNotBefore(m_foot, beforeChild);
153 m_foot = toRenderTableSection(child);
154 wrapInAnonymousSection = false;
158 case TABLE_ROW_GROUP:
159 resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
161 m_firstBody = toRenderTableSection(child);
162 wrapInAnonymousSection = false;
165 ASSERT_NOT_REACHED();
168 wrapInAnonymousSection = true;
170 if (child->isTableSection())
171 setNeedsSectionRecalc();
173 if (!wrapInAnonymousSection) {
174 if (beforeChild && beforeChild->parent() != this)
175 beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
177 RenderBox::addChild(child, beforeChild);
181 if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous() && !lastChild()->isBeforeContent()) {
182 lastChild()->addChild(child);
186 if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) {
187 RenderObject* section = beforeChild->previousSibling();
188 if (section && section->isTableSection() && section->isAnonymous()) {
189 section->addChild(child);
194 RenderObject* lastBox = beforeChild;
195 while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && needsTableSection(lastBox))
196 lastBox = lastBox->parent();
197 if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox)) {
198 if (beforeChild == lastBox)
199 beforeChild = lastBox->firstChild();
200 lastBox->addChild(child, beforeChild);
204 if (beforeChild && !beforeChild->isTableSection() && needsTableSection(beforeChild))
207 RenderTableSection* section = RenderTableSection::createAnonymousWithParentRenderer(this);
208 addChild(section, beforeChild);
209 section->addChild(child);
212 void RenderTable::addCaption(const RenderTableCaption* caption)
214 ASSERT(m_captions.find(caption) == kNotFound);
215 m_captions.append(const_cast<RenderTableCaption*>(caption));
218 void RenderTable::removeCaption(const RenderTableCaption* oldCaption)
220 size_t index = m_captions.find(oldCaption);
221 ASSERT(index != kNotFound);
222 if (index == kNotFound)
225 m_captions.remove(index);
228 void RenderTable::invalidateCachedColumns()
230 m_columnRenderersValid = false;
231 m_columnRenderers.resize(0);
234 void RenderTable::addColumn(const RenderTableCol*)
236 invalidateCachedColumns();
239 void RenderTable::removeColumn(const RenderTableCol*)
241 invalidateCachedColumns();
242 // We don't really need to recompute our sections, but we need to update our
243 // column count and whether we have a column. Currently, we only have one
244 // size-fit-all flag but we may have to consider splitting it.
245 setNeedsSectionRecalc();
248 void RenderTable::updateLogicalWidth()
250 recalcSectionsIfNeeded();
252 if (isOutOfFlowPositioned()) {
253 LogicalExtentComputedValues computedValues;
254 computePositionedLogicalWidth(computedValues);
255 setLogicalWidth(computedValues.m_extent);
256 setLogicalLeft(computedValues.m_position);
257 setMarginStart(computedValues.m_margins.m_start);
258 setMarginEnd(computedValues.m_margins.m_end);
261 RenderBlock* cb = containingBlock();
263 LayoutUnit availableLogicalWidth = containingBlockLogicalWidthForContent();
264 bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode();
265 LayoutUnit containerWidthInInlineDirection = hasPerpendicularContainingBlock ? perpendicularContainingBlockLogicalHeight() : availableLogicalWidth;
267 Length styleLogicalWidth = style()->logicalWidth();
268 if ((styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive()) || styleLogicalWidth.isIntrinsic())
269 setLogicalWidth(convertStyleLogicalWidthToComputedWidth(styleLogicalWidth, containerWidthInInlineDirection));
271 // Subtract out any fixed margins from our available width for auto width tables.
272 LayoutUnit marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth);
273 LayoutUnit marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth);
274 LayoutUnit marginTotal = marginStart + marginEnd;
276 // Subtract out our margins to get the available content width.
277 LayoutUnit availableContentLogicalWidth = max<LayoutUnit>(0, containerWidthInInlineDirection - marginTotal);
278 if (shrinkToAvoidFloats() && cb->containsFloats() && !hasPerpendicularContainingBlock)
279 availableContentLogicalWidth = shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, toRenderBlockFlow(cb));
281 // Ensure we aren't bigger than our available width.
282 setLogicalWidth(min<int>(availableContentLogicalWidth, maxPreferredLogicalWidth()));
285 // Ensure we aren't bigger than our max-width style.
286 Length styleMaxLogicalWidth = style()->logicalMaxWidth();
287 if ((styleMaxLogicalWidth.isSpecified() && !styleMaxLogicalWidth.isNegative()) || styleMaxLogicalWidth.isIntrinsic()) {
288 LayoutUnit computedMaxLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMaxLogicalWidth, availableLogicalWidth);
289 setLogicalWidth(min<int>(logicalWidth(), computedMaxLogicalWidth));
292 // Ensure we aren't smaller than our min preferred width. This MUST be done after 'max-width' as
293 // we ignore it if it means we wouldn't accomodate our content.
294 setLogicalWidth(max<int>(logicalWidth(), minPreferredLogicalWidth()));
296 // Ensure we aren't smaller than our min-width style.
297 Length styleMinLogicalWidth = style()->logicalMinWidth();
298 if ((styleMinLogicalWidth.isSpecified() && !styleMinLogicalWidth.isNegative()) || styleMinLogicalWidth.isIntrinsic()) {
299 LayoutUnit computedMinLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMinLogicalWidth, availableLogicalWidth);
300 setLogicalWidth(max<int>(logicalWidth(), computedMinLogicalWidth));
303 // Finally, with our true width determined, compute our margins for real.
306 if (!hasPerpendicularContainingBlock) {
307 LayoutUnit containerLogicalWidthForAutoMargins = availableLogicalWidth;
308 if (avoidsFloats() && cb->containsFloats())
309 containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidth();
310 ComputedMarginValues marginValues;
311 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() == style()->isLeftToRightDirection();
312 computeInlineDirectionMargins(cb, containerLogicalWidthForAutoMargins, logicalWidth(),
313 hasInvertedDirection ? marginValues.m_start : marginValues.m_end,
314 hasInvertedDirection ? marginValues.m_end : marginValues.m_start);
315 setMarginStart(marginValues.m_start);
316 setMarginEnd(marginValues.m_end);
318 setMarginStart(minimumValueForLength(style()->marginStart(), availableLogicalWidth));
319 setMarginEnd(minimumValueForLength(style()->marginEnd(), availableLogicalWidth));
322 // We should NEVER shrink the table below the min-content logical width, or else the table can't accomodate
323 // its own content which doesn't match CSS nor what authors expect.
324 // FIXME: When we convert to sub-pixel layout for tables we can remove the int conversion
325 // https://code.google.com/p/chromium/issues/detail?id=241198
326 ASSERT(logicalWidth().toInt() >= minPreferredLogicalWidth().toInt());
329 // This method takes a RenderStyle's logical width, min-width, or max-width length and computes its actual value.
330 LayoutUnit RenderTable::convertStyleLogicalWidthToComputedWidth(const Length& styleLogicalWidth, LayoutUnit availableWidth)
332 if (styleLogicalWidth.isIntrinsic())
333 return computeIntrinsicLogicalWidthUsing(styleLogicalWidth, availableWidth, bordersPaddingAndSpacingInRowDirection());
335 // HTML tables' width styles already include borders and paddings, but CSS tables' width styles do not.
336 LayoutUnit borders = 0;
337 bool isCSSTable = !node() || !node()->hasTagName(tableTag);
338 if (isCSSTable && styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive() && style()->boxSizing() == CONTENT_BOX)
339 borders = borderStart() + borderEnd() + (collapseBorders() ? LayoutUnit() : paddingStart() + paddingEnd());
341 return minimumValueForLength(styleLogicalWidth, availableWidth) + borders;
344 LayoutUnit RenderTable::convertStyleLogicalHeightToComputedHeight(const Length& styleLogicalHeight)
346 LayoutUnit borderAndPaddingBefore = borderBefore() + (collapseBorders() ? LayoutUnit() : paddingBefore());
347 LayoutUnit borderAndPaddingAfter = borderAfter() + (collapseBorders() ? LayoutUnit() : paddingAfter());
348 LayoutUnit borderAndPadding = borderAndPaddingBefore + borderAndPaddingAfter;
349 LayoutUnit computedLogicalHeight = 0;
350 if (styleLogicalHeight.isFixed()) {
351 // HTML tables size as though CSS height includes border/padding, CSS tables do not.
352 LayoutUnit borders = LayoutUnit();
353 // FIXME: We cannot apply box-sizing: content-box on <table> which other browsers allow.
354 if ((node() && node()->hasTagName(tableTag)) || style()->boxSizing() == BORDER_BOX) {
355 borders = borderAndPadding;
357 computedLogicalHeight = styleLogicalHeight.value() - borders;
358 } else if (styleLogicalHeight.isPercent())
359 computedLogicalHeight = computePercentageLogicalHeight(styleLogicalHeight);
360 else if (styleLogicalHeight.isIntrinsic())
361 computedLogicalHeight = computeIntrinsicLogicalContentHeightUsing(styleLogicalHeight, logicalHeight() - borderAndPadding, borderAndPadding);
363 ASSERT_NOT_REACHED();
364 return max<LayoutUnit>(0, computedLogicalHeight);
367 void RenderTable::layoutCaption(RenderTableCaption* caption)
369 LayoutRect captionRect(caption->frameRect());
371 if (caption->needsLayout()) {
372 // The margins may not be available but ensure the caption is at least located beneath any previous sibling caption
373 // so that it does not mistakenly think any floats in the previous caption intrude into it.
374 caption->setLogicalLocation(LayoutPoint(caption->marginStart(), collapsedMarginBeforeForChild(caption) + logicalHeight()));
375 // If RenderTableCaption ever gets a layout() function, use it here.
376 caption->layoutIfNeeded();
378 // Apply the margins to the location now that they are definitely available from layout
379 LayoutUnit captionLogicalTop = collapsedMarginBeforeForChild(caption) + logicalHeight();
380 if (view()->layoutState()->isPaginated()) {
381 captionLogicalTop += caption->paginationStrut();
382 caption->setPaginationStrut(0);
384 caption->setLogicalLocation(LayoutPoint(caption->marginStart(), captionLogicalTop));
386 if (!selfNeedsLayout() && caption->checkForRepaintDuringLayout())
387 caption->repaintDuringLayoutIfMoved(captionRect);
389 setLogicalHeight(logicalHeight() + caption->logicalHeight() + collapsedMarginBeforeForChild(caption) + collapsedMarginAfterForChild(caption));
392 void RenderTable::distributeExtraLogicalHeight(int extraLogicalHeight)
394 if (extraLogicalHeight <= 0)
397 // FIXME: Distribute the extra logical height between all table sections instead of giving it all to the first one.
398 if (RenderTableSection* section = firstBody())
399 extraLogicalHeight -= section->distributeExtraLogicalHeightToRows(extraLogicalHeight);
401 // FIXME: We really would like to enable this ASSERT to ensure that all the extra space has been distributed.
402 // However our current distribution algorithm does not round properly and thus we can have some remaining height.
403 // ASSERT(!topSection() || !extraLogicalHeight);
406 void RenderTable::simplifiedNormalFlowLayout()
408 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) {
409 section->layoutIfNeeded();
410 section->computeOverflowFromCells();
414 void RenderTable::layout()
416 ASSERT(needsLayout());
418 LayoutRectRecorder recorder(*this);
420 if (simplifiedLayout())
423 recalcSectionsIfNeeded();
424 // FIXME: We should do this recalc lazily in borderStart/borderEnd so that we don't have to make sure
425 // to call this before we call borderStart/borderEnd to avoid getting a stale value.
426 recalcBordersInRowDirection();
428 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
429 LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode());
433 LayoutUnit oldLogicalWidth = logicalWidth();
434 updateLogicalWidth();
436 SubtreeLayoutScope layouter(this);
438 if (logicalWidth() != oldLogicalWidth) {
439 for (unsigned i = 0; i < m_captions.size(); i++)
440 layouter.setNeedsLayout(m_captions[i]);
442 // FIXME: The optimisation below doesn't work since the internal table
443 // layout could have changed. we need to add a flag to the table
444 // layout that tells us if something has changed in the min max
445 // calculations to do it correctly.
446 // if ( oldWidth != width() || columns.size() + 1 != columnPos.size() )
447 m_tableLayout->layout();
449 LayoutUnit totalSectionLogicalHeight = 0;
450 LayoutUnit oldTableLogicalTop = 0;
451 for (unsigned i = 0; i < m_captions.size(); i++)
452 oldTableLogicalTop += m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter();
454 bool collapsing = collapseBorders();
456 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
457 if (child->isTableSection()) {
458 RenderTableSection* section = toRenderTableSection(child);
459 if (m_columnLogicalWidthChanged)
460 layouter.setChildNeedsLayout(section);
461 section->layoutIfNeeded();
462 totalSectionLogicalHeight += section->calcRowLogicalHeight();
464 section->recalcOuterBorder();
465 ASSERT(!section->needsLayout());
466 } else if (child->isRenderTableCol()) {
467 child->layoutIfNeeded();
468 ASSERT(!child->needsLayout());
470 // FIXME: We should never have other type of children (they should be wrapped in an
471 // anonymous table section) but our code is too crazy and this can happen in practice.
472 // Until this is fixed, let's make sure we don't leave non laid out children in the tree.
473 child->layoutIfNeeded();
477 // If any table section moved vertically, we will just repaint everything from that
478 // section down (it is quite unlikely that any of the following sections
480 bool sectionMoved = false;
481 LayoutUnit movedSectionLogicalTop = 0;
483 // FIXME: Collapse caption margin.
484 if (!m_captions.isEmpty()) {
485 for (unsigned i = 0; i < m_captions.size(); i++) {
486 if (m_captions[i]->style()->captionSide() == CAPBOTTOM)
488 layoutCaption(m_captions[i]);
490 if (logicalHeight() != oldTableLogicalTop) {
492 movedSectionLogicalTop = min(logicalHeight(), oldTableLogicalTop);
496 LayoutUnit borderAndPaddingBefore = borderBefore() + (collapsing ? LayoutUnit() : paddingBefore());
497 LayoutUnit borderAndPaddingAfter = borderAfter() + (collapsing ? LayoutUnit() : paddingAfter());
499 setLogicalHeight(logicalHeight() + borderAndPaddingBefore);
501 if (!isOutOfFlowPositioned())
502 updateLogicalHeight();
504 LayoutUnit computedLogicalHeight = 0;
506 Length logicalHeightLength = style()->logicalHeight();
507 if (logicalHeightLength.isIntrinsic() || (logicalHeightLength.isSpecified() && logicalHeightLength.isPositive()))
508 computedLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalHeightLength);
510 Length logicalMaxHeightLength = style()->logicalMaxHeight();
511 if (logicalMaxHeightLength.isIntrinsic() || (logicalMaxHeightLength.isSpecified() && !logicalMaxHeightLength.isNegative())) {
512 LayoutUnit computedMaxLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMaxHeightLength);
513 computedLogicalHeight = min(computedLogicalHeight, computedMaxLogicalHeight);
516 Length logicalMinHeightLength = style()->logicalMinHeight();
517 if (logicalMinHeightLength.isIntrinsic() || (logicalMinHeightLength.isSpecified() && !logicalMinHeightLength.isNegative())) {
518 LayoutUnit computedMinLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMinHeightLength);
519 computedLogicalHeight = max(computedLogicalHeight, computedMinLogicalHeight);
522 distributeExtraLogicalHeight(floorToInt(computedLogicalHeight - totalSectionLogicalHeight));
524 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section))
525 section->layoutRows();
527 if (!topSection() && computedLogicalHeight > totalSectionLogicalHeight && !document().inQuirksMode()) {
528 // Completely empty tables (with no sections or anything) should at least honor specified height
530 setLogicalHeight(logicalHeight() + computedLogicalHeight);
533 LayoutUnit sectionLogicalLeft = style()->isLeftToRightDirection() ? borderStart() : borderEnd();
535 sectionLogicalLeft += style()->isLeftToRightDirection() ? paddingStart() : paddingEnd();
537 // position the table sections
538 RenderTableSection* section = topSection();
540 if (!sectionMoved && section->logicalTop() != logicalHeight()) {
542 movedSectionLogicalTop = min(logicalHeight(), section->logicalTop()) + (style()->isHorizontalWritingMode() ? section->visualOverflowRect().y() : section->visualOverflowRect().x());
544 section->setLogicalLocation(LayoutPoint(sectionLogicalLeft, logicalHeight()));
546 setLogicalHeight(logicalHeight() + section->logicalHeight());
547 section = sectionBelow(section);
550 setLogicalHeight(logicalHeight() + borderAndPaddingAfter);
552 for (unsigned i = 0; i < m_captions.size(); i++) {
553 if (m_captions[i]->style()->captionSide() != CAPBOTTOM)
555 layoutCaption(m_captions[i]);
558 if (isOutOfFlowPositioned())
559 updateLogicalHeight();
561 // table can be containing block of positioned elements.
562 // FIXME: Only pass true if width or height changed.
563 layoutPositionedObjects(true);
565 updateLayerTransform();
567 // Layout was changed, so probably borders too.
568 invalidateCollapsedBorders();
570 computeOverflow(clientLogicalBottom());
574 if (view()->layoutState()->pageLogicalHeight())
575 setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(this, logicalTop()));
577 bool didFullRepaint = repainter.repaintAfterLayout();
578 // Repaint with our new bounds if they are different from our old bounds.
579 if (!RuntimeEnabledFeatures::repaintAfterLayoutEnabled()
580 && !didFullRepaint && sectionMoved) {
581 if (style()->isHorizontalWritingMode())
582 repaintRectangle(LayoutRect(visualOverflowRect().x(), movedSectionLogicalTop, visualOverflowRect().width(), visualOverflowRect().maxY() - movedSectionLogicalTop));
584 repaintRectangle(LayoutRect(movedSectionLogicalTop, visualOverflowRect().y(), visualOverflowRect().maxX() - movedSectionLogicalTop, visualOverflowRect().height()));
587 m_columnLogicalWidthChanged = false;
591 // Collect all the unique border values that we want to paint in a sorted list.
592 void RenderTable::recalcCollapsedBorders()
594 if (m_collapsedBordersValid)
596 m_collapsedBordersValid = true;
597 m_collapsedBorders.clear();
598 for (RenderObject* section = firstChild(); section; section = section->nextSibling()) {
599 if (!section->isTableSection())
601 for (RenderObject* row = section->firstChild(); row; row = row->nextSibling()) {
602 if (!row->isTableRow())
604 for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) {
605 if (!cell->isTableCell())
607 ASSERT(toRenderTableCell(cell)->table() == this);
608 toRenderTableCell(cell)->collectBorderValues(m_collapsedBorders);
612 RenderTableCell::sortBorderValues(m_collapsedBorders);
616 void RenderTable::addOverflowFromChildren()
618 // Add overflow from borders.
619 // Technically it's odd that we are incorporating the borders into layout overflow, which is only supposed to be about overflow from our
620 // descendant objects, but since tables don't support overflow:auto, this works out fine.
621 if (collapseBorders()) {
622 int rightBorderOverflow = width() + outerBorderRight() - borderRight();
623 int leftBorderOverflow = borderLeft() - outerBorderLeft();
624 int bottomBorderOverflow = height() + outerBorderBottom() - borderBottom();
625 int topBorderOverflow = borderTop() - outerBorderTop();
626 IntRect borderOverflowRect(leftBorderOverflow, topBorderOverflow, rightBorderOverflow - leftBorderOverflow, bottomBorderOverflow - topBorderOverflow);
627 if (borderOverflowRect != pixelSnappedBorderBoxRect()) {
628 addLayoutOverflow(borderOverflowRect);
629 addVisualOverflow(borderOverflowRect);
633 // Add overflow from our caption.
634 for (unsigned i = 0; i < m_captions.size(); i++)
635 addOverflowFromChild(m_captions[i]);
637 // Add overflow from our sections.
638 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section))
639 addOverflowFromChild(section);
642 void RenderTable::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
644 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
646 LayoutPoint adjustedPaintOffset = paintOffset + location();
648 PaintPhase paintPhase = paintInfo.phase;
651 LayoutRect overflowBox = visualOverflowRect();
652 flipForWritingMode(overflowBox);
653 overflowBox.inflate(maximalOutlineSize(paintInfo.phase));
654 overflowBox.moveBy(adjustedPaintOffset);
655 if (!overflowBox.intersects(paintInfo.rect))
659 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, ForceContentsClip);
660 paintObject(paintInfo, adjustedPaintOffset);
662 popContentsClip(paintInfo, paintPhase, adjustedPaintOffset);
665 void RenderTable::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
667 PaintPhase paintPhase = paintInfo.phase;
668 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE)
669 paintBoxDecorations(paintInfo, paintOffset);
671 if (paintPhase == PaintPhaseMask) {
672 paintMask(paintInfo, paintOffset);
676 // We're done. We don't bother painting any children.
677 if (paintPhase == PaintPhaseBlockBackground)
680 // We don't paint our own background, but we do let the kids paint their backgrounds.
681 if (paintPhase == PaintPhaseChildBlockBackgrounds)
682 paintPhase = PaintPhaseChildBlockBackground;
684 PaintInfo info(paintInfo);
685 info.phase = paintPhase;
686 info.updatePaintingRootForChildren(this);
688 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
689 if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) {
690 LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), paintOffset);
691 child->paint(info, childPoint);
695 if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) {
696 recalcCollapsedBorders();
697 // Using our cached sorted styles, we then do individual passes,
698 // painting each style of border from lowest precedence to highest precedence.
699 info.phase = PaintPhaseCollapsedTableBorders;
700 size_t count = m_collapsedBorders.size();
701 for (size_t i = 0; i < count; ++i) {
702 m_currentBorder = &m_collapsedBorders[i];
703 for (RenderTableSection* section = bottomSection(); section; section = sectionAbove(section)) {
704 LayoutPoint childPoint = flipForWritingModeForChild(section, paintOffset);
705 section->paint(info, childPoint);
712 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE)
713 paintOutline(paintInfo, LayoutRect(paintOffset, size()));
716 void RenderTable::subtractCaptionRect(LayoutRect& rect) const
718 for (unsigned i = 0; i < m_captions.size(); i++) {
719 LayoutUnit captionLogicalHeight = m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter();
720 bool captionIsBefore = (m_captions[i]->style()->captionSide() != CAPBOTTOM) ^ style()->isFlippedBlocksWritingMode();
721 if (style()->isHorizontalWritingMode()) {
722 rect.setHeight(rect.height() - captionLogicalHeight);
724 rect.move(0, captionLogicalHeight);
726 rect.setWidth(rect.width() - captionLogicalHeight);
728 rect.move(captionLogicalHeight, 0);
733 void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
735 if (!paintInfo.shouldPaintWithinRoot(this))
738 LayoutRect rect(paintOffset, size());
739 subtractCaptionRect(rect);
740 paintBoxDecorationsWithRect(paintInfo, paintOffset, rect);
743 void RenderTable::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
745 if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
748 LayoutRect rect(paintOffset, size());
749 subtractCaptionRect(rect);
751 paintMaskImages(paintInfo, rect);
754 void RenderTable::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) const
756 recalcSectionsIfNeeded();
757 // FIXME: Do the recalc in borderStart/borderEnd and make those const_cast this call.
758 // Then m_borderStart/m_borderEnd will be transparent a cache and it removes the possibility
759 // of reading out stale values.
760 const_cast<RenderTable*>(this)->recalcBordersInRowDirection();
761 // FIXME: Restructure the table layout code so that we can make this method const.
762 const_cast<RenderTable*>(this)->m_tableLayout->computeIntrinsicLogicalWidths(minWidth, maxWidth);
764 // FIXME: We should include captions widths here like we do in computePreferredLogicalWidths.
767 void RenderTable::computePreferredLogicalWidths()
769 ASSERT(preferredLogicalWidthsDirty());
771 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
773 int bordersPaddingAndSpacing = bordersPaddingAndSpacingInRowDirection();
774 m_minPreferredLogicalWidth += bordersPaddingAndSpacing;
775 m_maxPreferredLogicalWidth += bordersPaddingAndSpacing;
777 m_tableLayout->applyPreferredLogicalWidthQuirks(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
779 for (unsigned i = 0; i < m_captions.size(); i++)
780 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_captions[i]->minPreferredLogicalWidth());
782 RenderStyle* styleToUse = style();
783 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage or calc values for min-width.
784 if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) {
785 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
786 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
789 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage or calc values for maxWidth.
790 if (styleToUse->logicalMaxWidth().isFixed()) {
791 // We don't constrain m_minPreferredLogicalWidth as the table should be at least the size of its min-content, regardless of 'max-width'.
792 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value()));
793 m_maxPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
796 // FIXME: We should be adding borderAndPaddingLogicalWidth here, but m_tableLayout->computePreferredLogicalWidths already does,
797 // so a bunch of tests break doing this naively.
798 clearPreferredLogicalWidthsDirty();
801 RenderTableSection* RenderTable::topNonEmptySection() const
803 RenderTableSection* section = topSection();
804 if (section && !section->numRows())
805 section = sectionBelow(section, SkipEmptySections);
809 void RenderTable::splitColumn(unsigned position, unsigned firstSpan)
811 // We split the column at "position", taking "firstSpan" cells from the span.
812 ASSERT(m_columns[position].span > firstSpan);
813 m_columns.insert(position, ColumnStruct(firstSpan));
814 m_columns[position + 1].span -= firstSpan;
816 // Propagate the change in our columns representation to the sections that don't need
817 // cell recalc. If they do, they will be synced up directly with m_columns later.
818 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
819 if (!child->isTableSection())
822 RenderTableSection* section = toRenderTableSection(child);
823 if (section->needsCellRecalc())
826 section->splitColumn(position, firstSpan);
829 m_columnPos.grow(numEffCols() + 1);
832 void RenderTable::appendColumn(unsigned span)
834 unsigned newColumnIndex = m_columns.size();
835 m_columns.append(ColumnStruct(span));
837 // Unless the table has cell(s) with colspan that exceed the number of columns afforded
838 // by the other rows in the table we can use the fast path when mapping columns to effective columns.
839 m_hasCellColspanThatDeterminesTableWidth = m_hasCellColspanThatDeterminesTableWidth || span > 1;
841 // Propagate the change in our columns representation to the sections that don't need
842 // cell recalc. If they do, they will be synced up directly with m_columns later.
843 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
844 if (!child->isTableSection())
847 RenderTableSection* section = toRenderTableSection(child);
848 if (section->needsCellRecalc())
851 section->appendColumn(newColumnIndex);
854 m_columnPos.grow(numEffCols() + 1);
857 RenderTableCol* RenderTable::firstColumn() const
859 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
860 if (child->isRenderTableCol())
861 return toRenderTableCol(child);
867 void RenderTable::updateColumnCache() const
869 ASSERT(m_hasColElements);
870 ASSERT(m_columnRenderers.isEmpty());
871 ASSERT(!m_columnRenderersValid);
873 for (RenderTableCol* columnRenderer = firstColumn(); columnRenderer; columnRenderer = columnRenderer->nextColumn()) {
874 if (columnRenderer->isTableColumnGroupWithColumnChildren())
876 m_columnRenderers.append(columnRenderer);
878 m_columnRenderersValid = true;
881 RenderTableCol* RenderTable::slowColElement(unsigned col, bool* startEdge, bool* endEdge) const
883 ASSERT(m_hasColElements);
885 if (!m_columnRenderersValid)
888 unsigned columnCount = 0;
889 for (unsigned i = 0; i < m_columnRenderers.size(); i++) {
890 RenderTableCol* columnRenderer = m_columnRenderers[i];
891 unsigned span = columnRenderer->span();
892 unsigned startCol = columnCount;
894 unsigned endCol = columnCount + span - 1;
896 if (columnCount > col) {
898 *startEdge = startCol == col;
900 *endEdge = endCol == col;
901 return columnRenderer;
907 void RenderTable::recalcSections() const
909 ASSERT(m_needsSectionRecalc);
914 m_hasColElements = false;
915 m_hasCellColspanThatDeterminesTableWidth = hasCellColspanThatDeterminesTableWidth();
917 // We need to get valid pointers to caption, head, foot and first body again
918 RenderObject* nextSibling;
919 for (RenderObject* child = firstChild(); child; child = nextSibling) {
920 nextSibling = child->nextSibling();
921 switch (child->style()->display()) {
923 case TABLE_COLUMN_GROUP:
924 m_hasColElements = true;
926 case TABLE_HEADER_GROUP:
927 if (child->isTableSection()) {
928 RenderTableSection* section = toRenderTableSection(child);
931 else if (!m_firstBody)
932 m_firstBody = section;
933 section->recalcCellsIfNeeded();
936 case TABLE_FOOTER_GROUP:
937 if (child->isTableSection()) {
938 RenderTableSection* section = toRenderTableSection(child);
941 else if (!m_firstBody)
942 m_firstBody = section;
943 section->recalcCellsIfNeeded();
946 case TABLE_ROW_GROUP:
947 if (child->isTableSection()) {
948 RenderTableSection* section = toRenderTableSection(child);
950 m_firstBody = section;
951 section->recalcCellsIfNeeded();
959 // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section)
960 unsigned maxCols = 0;
961 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
962 if (child->isTableSection()) {
963 RenderTableSection* section = toRenderTableSection(child);
964 unsigned sectionCols = section->numColumns();
965 if (sectionCols > maxCols)
966 maxCols = sectionCols;
970 m_columns.resize(maxCols);
971 m_columnPos.resize(maxCols + 1);
973 ASSERT(selfNeedsLayout());
975 m_needsSectionRecalc = false;
978 int RenderTable::calcBorderStart() const
980 if (!collapseBorders())
981 return RenderBlock::borderStart();
983 // Determined by the first cell of the first row. See the CSS 2.1 spec, section 17.6.2.
987 unsigned borderWidth = 0;
989 const BorderValue& tableStartBorder = style()->borderStart();
990 if (tableStartBorder.style() == BHIDDEN)
992 if (tableStartBorder.style() > BHIDDEN)
993 borderWidth = tableStartBorder.width();
995 if (RenderTableCol* column = colElement(0)) {
996 // FIXME: We don't account for direction on columns and column groups.
997 const BorderValue& columnAdjoiningBorder = column->style()->borderStart();
998 if (columnAdjoiningBorder.style() == BHIDDEN)
1000 if (columnAdjoiningBorder.style() > BHIDDEN)
1001 borderWidth = max(borderWidth, columnAdjoiningBorder.width());
1002 // FIXME: This logic doesn't properly account for the first column in the first column-group case.
1005 if (const RenderTableSection* topNonEmptySection = this->topNonEmptySection()) {
1006 const BorderValue& sectionAdjoiningBorder = topNonEmptySection->borderAdjoiningTableStart();
1007 if (sectionAdjoiningBorder.style() == BHIDDEN)
1010 if (sectionAdjoiningBorder.style() > BHIDDEN)
1011 borderWidth = max(borderWidth, sectionAdjoiningBorder.width());
1013 if (const RenderTableCell* adjoiningStartCell = topNonEmptySection->firstRowCellAdjoiningTableStart()) {
1014 // FIXME: Make this work with perpendicular and flipped cells.
1015 const BorderValue& startCellAdjoiningBorder = adjoiningStartCell->borderAdjoiningTableStart();
1016 if (startCellAdjoiningBorder.style() == BHIDDEN)
1019 const BorderValue& firstRowAdjoiningBorder = adjoiningStartCell->row()->borderAdjoiningTableStart();
1020 if (firstRowAdjoiningBorder.style() == BHIDDEN)
1023 if (startCellAdjoiningBorder.style() > BHIDDEN)
1024 borderWidth = max(borderWidth, startCellAdjoiningBorder.width());
1025 if (firstRowAdjoiningBorder.style() > BHIDDEN)
1026 borderWidth = max(borderWidth, firstRowAdjoiningBorder.width());
1029 return (borderWidth + (style()->isLeftToRightDirection() ? 0 : 1)) / 2;
1032 int RenderTable::calcBorderEnd() const
1034 if (!collapseBorders())
1035 return RenderBlock::borderEnd();
1037 // Determined by the last cell of the first row. See the CSS 2.1 spec, section 17.6.2.
1041 unsigned borderWidth = 0;
1043 const BorderValue& tableEndBorder = style()->borderEnd();
1044 if (tableEndBorder.style() == BHIDDEN)
1046 if (tableEndBorder.style() > BHIDDEN)
1047 borderWidth = tableEndBorder.width();
1049 unsigned endColumn = numEffCols() - 1;
1050 if (RenderTableCol* column = colElement(endColumn)) {
1051 // FIXME: We don't account for direction on columns and column groups.
1052 const BorderValue& columnAdjoiningBorder = column->style()->borderEnd();
1053 if (columnAdjoiningBorder.style() == BHIDDEN)
1055 if (columnAdjoiningBorder.style() > BHIDDEN)
1056 borderWidth = max(borderWidth, columnAdjoiningBorder.width());
1057 // FIXME: This logic doesn't properly account for the last column in the last column-group case.
1060 if (const RenderTableSection* topNonEmptySection = this->topNonEmptySection()) {
1061 const BorderValue& sectionAdjoiningBorder = topNonEmptySection->borderAdjoiningTableEnd();
1062 if (sectionAdjoiningBorder.style() == BHIDDEN)
1065 if (sectionAdjoiningBorder.style() > BHIDDEN)
1066 borderWidth = max(borderWidth, sectionAdjoiningBorder.width());
1068 if (const RenderTableCell* adjoiningEndCell = topNonEmptySection->firstRowCellAdjoiningTableEnd()) {
1069 // FIXME: Make this work with perpendicular and flipped cells.
1070 const BorderValue& endCellAdjoiningBorder = adjoiningEndCell->borderAdjoiningTableEnd();
1071 if (endCellAdjoiningBorder.style() == BHIDDEN)
1074 const BorderValue& firstRowAdjoiningBorder = adjoiningEndCell->row()->borderAdjoiningTableEnd();
1075 if (firstRowAdjoiningBorder.style() == BHIDDEN)
1078 if (endCellAdjoiningBorder.style() > BHIDDEN)
1079 borderWidth = max(borderWidth, endCellAdjoiningBorder.width());
1080 if (firstRowAdjoiningBorder.style() > BHIDDEN)
1081 borderWidth = max(borderWidth, firstRowAdjoiningBorder.width());
1084 return (borderWidth + (style()->isLeftToRightDirection() ? 1 : 0)) / 2;
1087 void RenderTable::recalcBordersInRowDirection()
1089 // FIXME: We need to compute the collapsed before / after borders in the same fashion.
1090 m_borderStart = calcBorderStart();
1091 m_borderEnd = calcBorderEnd();
1094 int RenderTable::borderBefore() const
1096 if (collapseBorders()) {
1097 recalcSectionsIfNeeded();
1098 return outerBorderBefore();
1100 return RenderBlock::borderBefore();
1103 int RenderTable::borderAfter() const
1105 if (collapseBorders()) {
1106 recalcSectionsIfNeeded();
1107 return outerBorderAfter();
1109 return RenderBlock::borderAfter();
1112 int RenderTable::outerBorderBefore() const
1114 if (!collapseBorders())
1116 int borderWidth = 0;
1117 if (RenderTableSection* topSection = this->topSection()) {
1118 borderWidth = topSection->outerBorderBefore();
1119 if (borderWidth < 0)
1120 return 0; // Overridden by hidden
1122 const BorderValue& tb = style()->borderBefore();
1123 if (tb.style() == BHIDDEN)
1125 if (tb.style() > BHIDDEN)
1126 borderWidth = max<int>(borderWidth, tb.width() / 2);
1130 int RenderTable::outerBorderAfter() const
1132 if (!collapseBorders())
1134 int borderWidth = 0;
1136 if (RenderTableSection* section = bottomSection()) {
1137 borderWidth = section->outerBorderAfter();
1138 if (borderWidth < 0)
1139 return 0; // Overridden by hidden
1141 const BorderValue& tb = style()->borderAfter();
1142 if (tb.style() == BHIDDEN)
1144 if (tb.style() > BHIDDEN)
1145 borderWidth = max<int>(borderWidth, (tb.width() + 1) / 2);
1149 int RenderTable::outerBorderStart() const
1151 if (!collapseBorders())
1154 int borderWidth = 0;
1156 const BorderValue& tb = style()->borderStart();
1157 if (tb.style() == BHIDDEN)
1159 if (tb.style() > BHIDDEN)
1160 borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 0 : 1)) / 2;
1162 bool allHidden = true;
1163 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) {
1164 int sw = section->outerBorderStart();
1168 borderWidth = max(borderWidth, sw);
1176 int RenderTable::outerBorderEnd() const
1178 if (!collapseBorders())
1181 int borderWidth = 0;
1183 const BorderValue& tb = style()->borderEnd();
1184 if (tb.style() == BHIDDEN)
1186 if (tb.style() > BHIDDEN)
1187 borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 1 : 0)) / 2;
1189 bool allHidden = true;
1190 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) {
1191 int sw = section->outerBorderEnd();
1195 borderWidth = max(borderWidth, sw);
1203 RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const
1205 recalcSectionsIfNeeded();
1207 if (section == m_head)
1210 RenderObject* prevSection = section == m_foot ? lastChild() : section->previousSibling();
1211 while (prevSection) {
1212 if (prevSection->isTableSection() && prevSection != m_head && prevSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || toRenderTableSection(prevSection)->numRows()))
1214 prevSection = prevSection->previousSibling();
1216 if (!prevSection && m_head && (skipEmptySections == DoNotSkipEmptySections || m_head->numRows()))
1217 prevSection = m_head;
1218 return toRenderTableSection(prevSection);
1221 RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const
1223 recalcSectionsIfNeeded();
1225 if (section == m_foot)
1228 RenderObject* nextSection = section == m_head ? firstChild() : section->nextSibling();
1229 while (nextSection) {
1230 if (nextSection->isTableSection() && nextSection != m_head && nextSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || toRenderTableSection(nextSection)->numRows()))
1232 nextSection = nextSection->nextSibling();
1234 if (!nextSection && m_foot && (skipEmptySections == DoNotSkipEmptySections || m_foot->numRows()))
1235 nextSection = m_foot;
1236 return toRenderTableSection(nextSection);
1239 RenderTableSection* RenderTable::bottomSection() const
1241 recalcSectionsIfNeeded();
1246 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1247 if (child->isTableSection())
1248 return toRenderTableSection(child);
1254 RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
1256 recalcSectionsIfNeeded();
1258 // Find the section and row to look in
1259 unsigned r = cell->rowIndex();
1260 RenderTableSection* section = 0;
1261 unsigned rAbove = 0;
1263 // cell is not in the first row, so use the above row in its own section
1264 section = cell->section();
1267 section = sectionAbove(cell->section(), SkipEmptySections);
1269 ASSERT(section->numRows());
1270 rAbove = section->numRows() - 1;
1274 // Look up the cell in the section's grid, which requires effective col index
1276 unsigned effCol = colToEffCol(cell->col());
1277 RenderTableSection::CellStruct& aboveCell = section->cellAt(rAbove, effCol);
1278 return aboveCell.primaryCell();
1283 RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const
1285 recalcSectionsIfNeeded();
1287 // Find the section and row to look in
1288 unsigned r = cell->rowIndex() + cell->rowSpan() - 1;
1289 RenderTableSection* section = 0;
1290 unsigned rBelow = 0;
1291 if (r < cell->section()->numRows() - 1) {
1292 // The cell is not in the last row, so use the next row in the section.
1293 section = cell->section();
1296 section = sectionBelow(cell->section(), SkipEmptySections);
1301 // Look up the cell in the section's grid, which requires effective col index
1303 unsigned effCol = colToEffCol(cell->col());
1304 RenderTableSection::CellStruct& belowCell = section->cellAt(rBelow, effCol);
1305 return belowCell.primaryCell();
1310 RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const
1312 recalcSectionsIfNeeded();
1314 RenderTableSection* section = cell->section();
1315 unsigned effCol = colToEffCol(cell->col());
1319 // If we hit a colspan back up to a real cell.
1320 RenderTableSection::CellStruct& prevCell = section->cellAt(cell->rowIndex(), effCol - 1);
1321 return prevCell.primaryCell();
1324 RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const
1326 recalcSectionsIfNeeded();
1328 unsigned effCol = colToEffCol(cell->col() + cell->colSpan());
1329 if (effCol >= numEffCols())
1331 return cell->section()->primaryCellAt(cell->rowIndex(), effCol);
1334 RenderBlock* RenderTable::firstLineBlock() const
1339 void RenderTable::updateFirstLetter()
1343 int RenderTable::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1345 ASSERT(linePositionMode == PositionOnContainingLine);
1346 int baseline = firstLineBoxBaseline();
1347 if (baseline != -1) {
1349 return beforeMarginInLineDirection(direction) + baseline;
1353 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1356 int RenderTable::inlineBlockBaseline(LineDirectionMode) const
1358 // Tables are skipped when computing an inline-block's baseline.
1362 int RenderTable::firstLineBoxBaseline() const
1364 // The baseline of a 'table' is the same as the 'inline-table' baseline per CSS 3 Flexbox (CSS 2.1
1365 // doesn't define the baseline of a 'table' only an 'inline-table').
1366 // This is also needed to properly determine the baseline of a cell if it has a table child.
1368 if (isWritingModeRoot())
1371 recalcSectionsIfNeeded();
1373 const RenderTableSection* topNonEmptySection = this->topNonEmptySection();
1374 if (!topNonEmptySection)
1377 int baseline = topNonEmptySection->firstLineBoxBaseline();
1379 return topNonEmptySection->logicalTop() + baseline;
1381 // FIXME: A table row always has a baseline per CSS 2.1. Will this return the right value?
1385 LayoutRect RenderTable::overflowClipRect(const LayoutPoint& location, OverlayScrollbarSizeRelevancy relevancy)
1387 LayoutRect rect = RenderBlock::overflowClipRect(location, relevancy);
1389 // If we have a caption, expand the clip to include the caption.
1390 // FIXME: Technically this is wrong, but it's virtually impossible to fix this
1391 // for real until captions have been re-written.
1392 // FIXME: This code assumes (like all our other caption code) that only top/bottom are
1393 // supported. When we actually support left/right and stop mapping them to top/bottom,
1394 // we might have to hack this code first (depending on what order we do these bug fixes in).
1395 if (!m_captions.isEmpty()) {
1396 if (style()->isHorizontalWritingMode()) {
1397 rect.setHeight(height());
1398 rect.setY(location.y());
1400 rect.setWidth(width());
1401 rect.setX(location.x());
1408 bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1410 LayoutPoint adjustedLocation = accumulatedOffset + location();
1412 // Check kids first.
1413 if (!hasOverflowClip() || locationInContainer.intersects(overflowClipRect(adjustedLocation))) {
1414 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1415 if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) {
1416 LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), adjustedLocation);
1417 if (child->nodeAtPoint(request, result, locationInContainer, childPoint, action)) {
1418 updateHitTestResult(result, toLayoutPoint(locationInContainer.point() - childPoint));
1425 // Check our bounds next.
1426 LayoutRect boundsRect(adjustedLocation, size());
1427 if (visibleToHitTestRequest(request) && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && locationInContainer.intersects(boundsRect)) {
1428 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(adjustedLocation)));
1429 if (!result.addNodeToRectBasedTestResult(node(), request, locationInContainer, boundsRect))
1436 RenderTable* RenderTable::createAnonymousWithParentRenderer(const RenderObject* parent)
1438 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE);
1439 RenderTable* newTable = new RenderTable(0);
1440 newTable->setDocumentForAnonymous(&parent->document());
1441 newTable->setStyle(newStyle.release());
1445 const BorderValue& RenderTable::tableStartBorderAdjoiningCell(const RenderTableCell* cell) const
1447 ASSERT(cell->isFirstOrLastCellInRow());
1448 if (hasSameDirectionAs(cell->row()))
1449 return style()->borderStart();
1451 return style()->borderEnd();
1454 const BorderValue& RenderTable::tableEndBorderAdjoiningCell(const RenderTableCell* cell) const
1456 ASSERT(cell->isFirstOrLastCellInRow());
1457 if (hasSameDirectionAs(cell->row()))
1458 return style()->borderEnd();
1460 return style()->borderStart();