This source file includes following definitions.
- m_borderEnd
- styleDidChange
- resetSectionPointerIfNotBefore
- needsTableSection
- addChild
- addCaption
- removeCaption
- invalidateCachedColumns
- addColumn
- removeColumn
- updateLogicalWidth
- convertStyleLogicalWidthToComputedWidth
- convertStyleLogicalHeightToComputedHeight
- layoutCaption
- distributeExtraLogicalHeight
- simplifiedNormalFlowLayout
- layout
- recalcCollapsedBorders
- addOverflowFromChildren
- paint
- paintObject
- subtractCaptionRect
- paintBoxDecorations
- paintMask
- computeIntrinsicLogicalWidths
- computePreferredLogicalWidths
- topNonEmptySection
- splitColumn
- appendColumn
- firstColumn
- updateColumnCache
- slowColElement
- recalcSections
- calcBorderStart
- calcBorderEnd
- recalcBordersInRowDirection
- borderBefore
- borderAfter
- outerBorderBefore
- outerBorderAfter
- outerBorderStart
- outerBorderEnd
- sectionAbove
- sectionBelow
- bottomSection
- cellAbove
- cellBelow
- cellBefore
- cellAfter
- firstLineBlock
- updateFirstLetter
- baselinePosition
- inlineBlockBaseline
- firstLineBoxBaseline
- overflowClipRect
- nodeAtPoint
- createAnonymousWithParentRenderer
- tableStartBorderAdjoiningCell
- tableEndBorderAdjoiningCell
#include "config.h"
#include "core/rendering/RenderTable.h"
#include "HTMLNames.h"
#include "core/dom/Document.h"
#include "core/frame/FrameView.h"
#include "core/html/HTMLTableElement.h"
#include "core/rendering/AutoTableLayout.h"
#include "core/rendering/FastTextAutosizer.h"
#include "core/rendering/FixedTableLayout.h"
#include "core/rendering/GraphicsContextAnnotator.h"
#include "core/rendering/HitTestResult.h"
#include "core/rendering/LayoutRectRecorder.h"
#include "core/rendering/LayoutRepainter.h"
#include "core/rendering/RenderLayer.h"
#include "core/rendering/RenderTableCaption.h"
#include "core/rendering/RenderTableCell.h"
#include "core/rendering/RenderTableCol.h"
#include "core/rendering/RenderTableSection.h"
#include "core/rendering/RenderView.h"
#include "core/rendering/SubtreeLayoutScope.h"
#include "core/rendering/style/StyleInheritedData.h"
#include "platform/graphics/GraphicsContextStateSaver.h"
using namespace std;
namespace WebCore {
using namespace HTMLNames;
RenderTable::RenderTable(Element* element)
: RenderBlock(element)
, m_head(0)
, m_foot(0)
, m_firstBody(0)
, m_currentBorder(0)
, m_collapsedBordersValid(false)
, m_hasColElements(false)
, m_needsSectionRecalc(false)
, m_columnLogicalWidthChanged(false)
, m_columnRenderersValid(false)
, m_hasCellColspanThatDeterminesTableWidth(false)
, m_hSpacing(0)
, m_vSpacing(0)
, m_borderStart(0)
, m_borderEnd(0)
{
setChildrenInline(false);
m_columnPos.fill(0, 1);
}
RenderTable::~RenderTable()
{
}
void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderBlock::styleDidChange(diff, oldStyle);
propagateStyleToAnonymousChildren();
ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO;
m_hSpacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing();
m_vSpacing = collapseBorders() ? 0 : style()->verticalBorderSpacing();
m_columnPos[0] = m_hSpacing;
if (!m_tableLayout || style()->tableLayout() != oldTableLayout) {
if (m_tableLayout)
m_tableLayout->willChangeTableLayout();
if (style()->tableLayout() == TFIXED && !style()->logicalWidth().isAuto())
m_tableLayout = adoptPtr(new FixedTableLayout(this));
else
m_tableLayout = adoptPtr(new AutoTableLayout(this));
}
if (!needsLayout() && oldStyle && oldStyle->border() != style()->border())
invalidateCollapsedBorders();
}
static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before)
{
if (!before || !ptr)
return;
RenderObject* o = before->previousSibling();
while (o && o != ptr)
o = o->previousSibling();
if (!o)
ptr = 0;
}
static inline bool needsTableSection(RenderObject* object)
{
EDisplay display = object->style()->display();
return display != TABLE_CAPTION && display != TABLE_COLUMN_GROUP && display != TABLE_COLUMN;
}
void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild)
{
bool wrapInAnonymousSection = !child->isOutOfFlowPositioned();
if (child->isTableCaption())
wrapInAnonymousSection = false;
else if (child->isRenderTableCol()) {
m_hasColElements = true;
wrapInAnonymousSection = false;
} else if (child->isTableSection()) {
switch (child->style()->display()) {
case TABLE_HEADER_GROUP:
resetSectionPointerIfNotBefore(m_head, beforeChild);
if (!m_head) {
m_head = toRenderTableSection(child);
} else {
resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
if (!m_firstBody)
m_firstBody = toRenderTableSection(child);
}
wrapInAnonymousSection = false;
break;
case TABLE_FOOTER_GROUP:
resetSectionPointerIfNotBefore(m_foot, beforeChild);
if (!m_foot) {
m_foot = toRenderTableSection(child);
wrapInAnonymousSection = false;
break;
}
case TABLE_ROW_GROUP:
resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
if (!m_firstBody)
m_firstBody = toRenderTableSection(child);
wrapInAnonymousSection = false;
break;
default:
ASSERT_NOT_REACHED();
}
} else
wrapInAnonymousSection = true;
if (child->isTableSection())
setNeedsSectionRecalc();
if (!wrapInAnonymousSection) {
if (beforeChild && beforeChild->parent() != this)
beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
RenderBox::addChild(child, beforeChild);
return;
}
if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous() && !lastChild()->isBeforeContent()) {
lastChild()->addChild(child);
return;
}
if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) {
RenderObject* section = beforeChild->previousSibling();
if (section && section->isTableSection() && section->isAnonymous()) {
section->addChild(child);
return;
}
}
RenderObject* lastBox = beforeChild;
while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && needsTableSection(lastBox))
lastBox = lastBox->parent();
if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox)) {
if (beforeChild == lastBox)
beforeChild = lastBox->firstChild();
lastBox->addChild(child, beforeChild);
return;
}
if (beforeChild && !beforeChild->isTableSection() && needsTableSection(beforeChild))
beforeChild = 0;
RenderTableSection* section = RenderTableSection::createAnonymousWithParentRenderer(this);
addChild(section, beforeChild);
section->addChild(child);
}
void RenderTable::addCaption(const RenderTableCaption* caption)
{
ASSERT(m_captions.find(caption) == kNotFound);
m_captions.append(const_cast<RenderTableCaption*>(caption));
}
void RenderTable::removeCaption(const RenderTableCaption* oldCaption)
{
size_t index = m_captions.find(oldCaption);
ASSERT(index != kNotFound);
if (index == kNotFound)
return;
m_captions.remove(index);
}
void RenderTable::invalidateCachedColumns()
{
m_columnRenderersValid = false;
m_columnRenderers.resize(0);
}
void RenderTable::addColumn(const RenderTableCol*)
{
invalidateCachedColumns();
}
void RenderTable::removeColumn(const RenderTableCol*)
{
invalidateCachedColumns();
setNeedsSectionRecalc();
}
void RenderTable::updateLogicalWidth()
{
recalcSectionsIfNeeded();
if (isOutOfFlowPositioned()) {
LogicalExtentComputedValues computedValues;
computePositionedLogicalWidth(computedValues);
setLogicalWidth(computedValues.m_extent);
setLogicalLeft(computedValues.m_position);
setMarginStart(computedValues.m_margins.m_start);
setMarginEnd(computedValues.m_margins.m_end);
}
RenderBlock* cb = containingBlock();
LayoutUnit availableLogicalWidth = containingBlockLogicalWidthForContent();
bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode();
LayoutUnit containerWidthInInlineDirection = hasPerpendicularContainingBlock ? perpendicularContainingBlockLogicalHeight() : availableLogicalWidth;
Length styleLogicalWidth = style()->logicalWidth();
if ((styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive()) || styleLogicalWidth.isIntrinsic())
setLogicalWidth(convertStyleLogicalWidthToComputedWidth(styleLogicalWidth, containerWidthInInlineDirection));
else {
LayoutUnit marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth);
LayoutUnit marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth);
LayoutUnit marginTotal = marginStart + marginEnd;
LayoutUnit availableContentLogicalWidth = max<LayoutUnit>(0, containerWidthInInlineDirection - marginTotal);
if (shrinkToAvoidFloats() && cb->containsFloats() && !hasPerpendicularContainingBlock)
availableContentLogicalWidth = shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, toRenderBlockFlow(cb));
setLogicalWidth(min<int>(availableContentLogicalWidth, maxPreferredLogicalWidth()));
}
Length styleMaxLogicalWidth = style()->logicalMaxWidth();
if ((styleMaxLogicalWidth.isSpecified() && !styleMaxLogicalWidth.isNegative()) || styleMaxLogicalWidth.isIntrinsic()) {
LayoutUnit computedMaxLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMaxLogicalWidth, availableLogicalWidth);
setLogicalWidth(min<int>(logicalWidth(), computedMaxLogicalWidth));
}
setLogicalWidth(max<int>(logicalWidth(), minPreferredLogicalWidth()));
Length styleMinLogicalWidth = style()->logicalMinWidth();
if ((styleMinLogicalWidth.isSpecified() && !styleMinLogicalWidth.isNegative()) || styleMinLogicalWidth.isIntrinsic()) {
LayoutUnit computedMinLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMinLogicalWidth, availableLogicalWidth);
setLogicalWidth(max<int>(logicalWidth(), computedMinLogicalWidth));
}
setMarginStart(0);
setMarginEnd(0);
if (!hasPerpendicularContainingBlock) {
ComputedMarginValues marginValues;
bool hasInvertedDirection = cb->style()->isLeftToRightDirection() == style()->isLeftToRightDirection();
computeInlineDirectionMargins(cb, availableLogicalWidth, logicalWidth(),
hasInvertedDirection ? marginValues.m_start : marginValues.m_end,
hasInvertedDirection ? marginValues.m_end : marginValues.m_start);
setMarginStart(marginValues.m_start);
setMarginEnd(marginValues.m_end);
} else {
setMarginStart(minimumValueForLength(style()->marginStart(), availableLogicalWidth));
setMarginEnd(minimumValueForLength(style()->marginEnd(), availableLogicalWidth));
}
ASSERT(logicalWidth().toInt() >= minPreferredLogicalWidth().toInt());
}
LayoutUnit RenderTable::convertStyleLogicalWidthToComputedWidth(const Length& styleLogicalWidth, LayoutUnit availableWidth)
{
if (styleLogicalWidth.isIntrinsic())
return computeIntrinsicLogicalWidthUsing(styleLogicalWidth, availableWidth, bordersPaddingAndSpacingInRowDirection());
LayoutUnit borders = 0;
bool isCSSTable = !isHTMLTableElement(node());
if (isCSSTable && styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive() && style()->boxSizing() == CONTENT_BOX)
borders = borderStart() + borderEnd() + (collapseBorders() ? LayoutUnit() : paddingStart() + paddingEnd());
return minimumValueForLength(styleLogicalWidth, availableWidth) + borders;
}
LayoutUnit RenderTable::convertStyleLogicalHeightToComputedHeight(const Length& styleLogicalHeight)
{
LayoutUnit borderAndPaddingBefore = borderBefore() + (collapseBorders() ? LayoutUnit() : paddingBefore());
LayoutUnit borderAndPaddingAfter = borderAfter() + (collapseBorders() ? LayoutUnit() : paddingAfter());
LayoutUnit borderAndPadding = borderAndPaddingBefore + borderAndPaddingAfter;
LayoutUnit computedLogicalHeight = 0;
if (styleLogicalHeight.isFixed()) {
LayoutUnit borders = LayoutUnit();
if (isHTMLTableElement(node()) || style()->boxSizing() == BORDER_BOX) {
borders = borderAndPadding;
}
computedLogicalHeight = styleLogicalHeight.value() - borders;
} else if (styleLogicalHeight.isPercent())
computedLogicalHeight = computePercentageLogicalHeight(styleLogicalHeight);
else if (styleLogicalHeight.isIntrinsic())
computedLogicalHeight = computeIntrinsicLogicalContentHeightUsing(styleLogicalHeight, logicalHeight() - borderAndPadding, borderAndPadding);
else
ASSERT_NOT_REACHED();
return max<LayoutUnit>(0, computedLogicalHeight);
}
void RenderTable::layoutCaption(RenderTableCaption* caption)
{
LayoutRect captionRect(caption->frameRect());
if (caption->needsLayout()) {
caption->setLogicalLocation(LayoutPoint(caption->marginStart(), collapsedMarginBeforeForChild(caption) + logicalHeight()));
caption->layoutIfNeeded();
}
LayoutUnit captionLogicalTop = collapsedMarginBeforeForChild(caption) + logicalHeight();
if (view()->layoutState()->isPaginated()) {
captionLogicalTop += caption->paginationStrut();
caption->setPaginationStrut(0);
}
caption->setLogicalLocation(LayoutPoint(caption->marginStart(), captionLogicalTop));
if (!selfNeedsLayout() && caption->checkForRepaintDuringLayout())
caption->repaintDuringLayoutIfMoved(captionRect);
setLogicalHeight(logicalHeight() + caption->logicalHeight() + collapsedMarginBeforeForChild(caption) + collapsedMarginAfterForChild(caption));
}
void RenderTable::distributeExtraLogicalHeight(int extraLogicalHeight)
{
if (extraLogicalHeight <= 0)
return;
if (RenderTableSection* section = firstBody())
extraLogicalHeight -= section->distributeExtraLogicalHeightToRows(extraLogicalHeight);
}
void RenderTable::simplifiedNormalFlowLayout()
{
for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) {
section->layoutIfNeeded();
section->computeOverflowFromCells();
}
}
void RenderTable::layout()
{
ASSERT(needsLayout());
LayoutRectRecorder recorder(*this);
if (simplifiedLayout())
return;
FastTextAutosizer::LayoutScope fastTextAutosizerLayoutScope(this);
recalcSectionsIfNeeded();
recalcBordersInRowDirection();
LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
SubtreeLayoutScope layouter(this);
bool sectionMoved = false;
LayoutUnit movedSectionLogicalTop = 0;
{
LayoutStateMaintainer statePusher(*this, locationOffset());
setLogicalHeight(0);
LayoutUnit oldLogicalWidth = logicalWidth();
updateLogicalWidth();
if (logicalWidth() != oldLogicalWidth) {
for (unsigned i = 0; i < m_captions.size(); i++)
layouter.setNeedsLayout(m_captions[i]);
}
m_tableLayout->layout();
LayoutUnit totalSectionLogicalHeight = 0;
LayoutUnit oldTableLogicalTop = 0;
for (unsigned i = 0; i < m_captions.size(); i++)
oldTableLogicalTop += m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter();
bool collapsing = collapseBorders();
for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
if (child->isTableSection()) {
RenderTableSection* section = toRenderTableSection(child);
if (m_columnLogicalWidthChanged)
layouter.setChildNeedsLayout(section);
section->layoutIfNeeded();
totalSectionLogicalHeight += section->calcRowLogicalHeight();
if (collapsing)
section->recalcOuterBorder();
ASSERT(!section->needsLayout());
} else if (child->isRenderTableCol()) {
child->layoutIfNeeded();
ASSERT(!child->needsLayout());
} else {
child->layoutIfNeeded();
}
}
if (!m_captions.isEmpty()) {
for (unsigned i = 0; i < m_captions.size(); i++) {
if (m_captions[i]->style()->captionSide() == CAPBOTTOM)
continue;
layoutCaption(m_captions[i]);
}
if (logicalHeight() != oldTableLogicalTop) {
sectionMoved = true;
movedSectionLogicalTop = min(logicalHeight(), oldTableLogicalTop);
}
}
LayoutUnit borderAndPaddingBefore = borderBefore() + (collapsing ? LayoutUnit() : paddingBefore());
LayoutUnit borderAndPaddingAfter = borderAfter() + (collapsing ? LayoutUnit() : paddingAfter());
setLogicalHeight(logicalHeight() + borderAndPaddingBefore);
if (!isOutOfFlowPositioned())
updateLogicalHeight();
LayoutUnit computedLogicalHeight = 0;
Length logicalHeightLength = style()->logicalHeight();
if (logicalHeightLength.isIntrinsic() || (logicalHeightLength.isSpecified() && logicalHeightLength.isPositive()))
computedLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalHeightLength);
Length logicalMaxHeightLength = style()->logicalMaxHeight();
if (logicalMaxHeightLength.isIntrinsic() || (logicalMaxHeightLength.isSpecified() && !logicalMaxHeightLength.isNegative())) {
LayoutUnit computedMaxLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMaxHeightLength);
computedLogicalHeight = min(computedLogicalHeight, computedMaxLogicalHeight);
}
Length logicalMinHeightLength = style()->logicalMinHeight();
if (logicalMinHeightLength.isIntrinsic() || (logicalMinHeightLength.isSpecified() && !logicalMinHeightLength.isNegative())) {
LayoutUnit computedMinLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMinHeightLength);
computedLogicalHeight = max(computedLogicalHeight, computedMinLogicalHeight);
}
distributeExtraLogicalHeight(floorToInt(computedLogicalHeight - totalSectionLogicalHeight));
for (RenderTableSection* section = topSection(); section; section = sectionBelow(section))
section->layoutRows();
if (!topSection() && computedLogicalHeight > totalSectionLogicalHeight && !document().inQuirksMode()) {
setLogicalHeight(logicalHeight() + computedLogicalHeight);
}
LayoutUnit sectionLogicalLeft = style()->isLeftToRightDirection() ? borderStart() : borderEnd();
if (!collapsing)
sectionLogicalLeft += style()->isLeftToRightDirection() ? paddingStart() : paddingEnd();
RenderTableSection* section = topSection();
while (section) {
if (!sectionMoved && section->logicalTop() != logicalHeight()) {
sectionMoved = true;
movedSectionLogicalTop = min(logicalHeight(), section->logicalTop()) + (style()->isHorizontalWritingMode() ? section->visualOverflowRect().y() : section->visualOverflowRect().x());
}
section->setLogicalLocation(LayoutPoint(sectionLogicalLeft, logicalHeight()));
setLogicalHeight(logicalHeight() + section->logicalHeight());
section = sectionBelow(section);
}
setLogicalHeight(logicalHeight() + borderAndPaddingAfter);
for (unsigned i = 0; i < m_captions.size(); i++) {
if (m_captions[i]->style()->captionSide() != CAPBOTTOM)
continue;
layoutCaption(m_captions[i]);
}
if (isOutOfFlowPositioned())
updateLogicalHeight();
layoutPositionedObjects(true);
updateLayerTransform();
invalidateCollapsedBorders();
computeOverflow(clientLogicalBottom());
}
if (view()->layoutState()->pageLogicalHeight())
setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(*this, logicalTop()));
bool didFullRepaint = repainter.repaintAfterLayout();
if (!RuntimeEnabledFeatures::repaintAfterLayoutEnabled()
&& !didFullRepaint && sectionMoved) {
if (style()->isHorizontalWritingMode())
repaintRectangle(LayoutRect(visualOverflowRect().x(), movedSectionLogicalTop, visualOverflowRect().width(), visualOverflowRect().maxY() - movedSectionLogicalTop));
else
repaintRectangle(LayoutRect(movedSectionLogicalTop, visualOverflowRect().y(), visualOverflowRect().maxX() - movedSectionLogicalTop, visualOverflowRect().height()));
}
m_columnLogicalWidthChanged = false;
clearNeedsLayout();
}
void RenderTable::recalcCollapsedBorders()
{
if (m_collapsedBordersValid)
return;
m_collapsedBordersValid = true;
m_collapsedBorders.clear();
for (RenderObject* section = firstChild(); section; section = section->nextSibling()) {
if (!section->isTableSection())
continue;
for (RenderObject* row = section->firstChild(); row; row = row->nextSibling()) {
if (!row->isTableRow())
continue;
for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) {
if (!cell->isTableCell())
continue;
ASSERT(toRenderTableCell(cell)->table() == this);
toRenderTableCell(cell)->collectBorderValues(m_collapsedBorders);
}
}
}
RenderTableCell::sortBorderValues(m_collapsedBorders);
}
void RenderTable::addOverflowFromChildren()
{
if (collapseBorders()) {
int rightBorderOverflow = width() + outerBorderRight() - borderRight();
int leftBorderOverflow = borderLeft() - outerBorderLeft();
int bottomBorderOverflow = height() + outerBorderBottom() - borderBottom();
int topBorderOverflow = borderTop() - outerBorderTop();
IntRect borderOverflowRect(leftBorderOverflow, topBorderOverflow, rightBorderOverflow - leftBorderOverflow, bottomBorderOverflow - topBorderOverflow);
if (borderOverflowRect != pixelSnappedBorderBoxRect()) {
addLayoutOverflow(borderOverflowRect);
addVisualOverflow(borderOverflowRect);
}
}
for (unsigned i = 0; i < m_captions.size(); i++)
addOverflowFromChild(m_captions[i]);
for (RenderTableSection* section = topSection(); section; section = sectionBelow(section))
addOverflowFromChild(section);
}
void RenderTable::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
LayoutPoint adjustedPaintOffset = paintOffset + location();
PaintPhase paintPhase = paintInfo.phase;
if (!isRoot()) {
LayoutRect overflowBox = visualOverflowRect();
flipForWritingMode(overflowBox);
overflowBox.moveBy(adjustedPaintOffset);
if (!overflowBox.intersects(paintInfo.rect))
return;
}
bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, ForceContentsClip);
paintObject(paintInfo, adjustedPaintOffset);
if (pushedClip)
popContentsClip(paintInfo, paintPhase, adjustedPaintOffset);
}
void RenderTable::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
PaintPhase paintPhase = paintInfo.phase;
if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE)
paintBoxDecorations(paintInfo, paintOffset);
if (paintPhase == PaintPhaseMask) {
paintMask(paintInfo, paintOffset);
return;
}
if (paintPhase == PaintPhaseBlockBackground)
return;
if (paintPhase == PaintPhaseChildBlockBackgrounds)
paintPhase = PaintPhaseChildBlockBackground;
PaintInfo info(paintInfo);
info.phase = paintPhase;
info.updatePaintingRootForChildren(this);
for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) {
LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), paintOffset);
child->paint(info, childPoint);
}
}
if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) {
recalcCollapsedBorders();
info.phase = PaintPhaseCollapsedTableBorders;
size_t count = m_collapsedBorders.size();
for (size_t i = 0; i < count; ++i) {
m_currentBorder = &m_collapsedBorders[i];
for (RenderTableSection* section = bottomSection(); section; section = sectionAbove(section)) {
LayoutPoint childPoint = flipForWritingModeForChild(section, paintOffset);
section->paint(info, childPoint);
}
}
m_currentBorder = 0;
}
if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE)
paintOutline(paintInfo, LayoutRect(paintOffset, size()));
}
void RenderTable::subtractCaptionRect(LayoutRect& rect) const
{
for (unsigned i = 0; i < m_captions.size(); i++) {
LayoutUnit captionLogicalHeight = m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter();
bool captionIsBefore = (m_captions[i]->style()->captionSide() != CAPBOTTOM) ^ style()->isFlippedBlocksWritingMode();
if (style()->isHorizontalWritingMode()) {
rect.setHeight(rect.height() - captionLogicalHeight);
if (captionIsBefore)
rect.move(0, captionLogicalHeight);
} else {
rect.setWidth(rect.width() - captionLogicalHeight);
if (captionIsBefore)
rect.move(captionLogicalHeight, 0);
}
}
}
void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (!paintInfo.shouldPaintWithinRoot(this))
return;
LayoutRect rect(paintOffset, size());
subtractCaptionRect(rect);
paintBoxDecorationsWithRect(paintInfo, paintOffset, rect);
}
void RenderTable::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
return;
LayoutRect rect(paintOffset, size());
subtractCaptionRect(rect);
paintMaskImages(paintInfo, rect);
}
void RenderTable::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) const
{
recalcSectionsIfNeeded();
const_cast<RenderTable*>(this)->recalcBordersInRowDirection();
const_cast<RenderTable*>(this)->m_tableLayout->computeIntrinsicLogicalWidths(minWidth, maxWidth);
}
void RenderTable::computePreferredLogicalWidths()
{
ASSERT(preferredLogicalWidthsDirty());
computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
int bordersPaddingAndSpacing = bordersPaddingAndSpacingInRowDirection();
m_minPreferredLogicalWidth += bordersPaddingAndSpacing;
m_maxPreferredLogicalWidth += bordersPaddingAndSpacing;
m_tableLayout->applyPreferredLogicalWidthQuirks(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
for (unsigned i = 0; i < m_captions.size(); i++)
m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_captions[i]->minPreferredLogicalWidth());
RenderStyle* styleToUse = style();
if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) {
m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
}
if (styleToUse->logicalMaxWidth().isFixed()) {
m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value()));
m_maxPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
}
clearPreferredLogicalWidthsDirty();
}
RenderTableSection* RenderTable::topNonEmptySection() const
{
RenderTableSection* section = topSection();
if (section && !section->numRows())
section = sectionBelow(section, SkipEmptySections);
return section;
}
void RenderTable::splitColumn(unsigned position, unsigned firstSpan)
{
ASSERT(m_columns[position].span > firstSpan);
m_columns.insert(position, ColumnStruct(firstSpan));
m_columns[position + 1].span -= firstSpan;
for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
if (!child->isTableSection())
continue;
RenderTableSection* section = toRenderTableSection(child);
if (section->needsCellRecalc())
continue;
section->splitColumn(position, firstSpan);
}
m_columnPos.grow(numEffCols() + 1);
}
void RenderTable::appendColumn(unsigned span)
{
unsigned newColumnIndex = m_columns.size();
m_columns.append(ColumnStruct(span));
m_hasCellColspanThatDeterminesTableWidth = m_hasCellColspanThatDeterminesTableWidth || span > 1;
for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
if (!child->isTableSection())
continue;
RenderTableSection* section = toRenderTableSection(child);
if (section->needsCellRecalc())
continue;
section->appendColumn(newColumnIndex);
}
m_columnPos.grow(numEffCols() + 1);
}
RenderTableCol* RenderTable::firstColumn() const
{
for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
if (child->isRenderTableCol())
return toRenderTableCol(child);
}
return 0;
}
void RenderTable::updateColumnCache() const
{
ASSERT(m_hasColElements);
ASSERT(m_columnRenderers.isEmpty());
ASSERT(!m_columnRenderersValid);
for (RenderTableCol* columnRenderer = firstColumn(); columnRenderer; columnRenderer = columnRenderer->nextColumn()) {
if (columnRenderer->isTableColumnGroupWithColumnChildren())
continue;
m_columnRenderers.append(columnRenderer);
}
m_columnRenderersValid = true;
}
RenderTableCol* RenderTable::slowColElement(unsigned col, bool* startEdge, bool* endEdge) const
{
ASSERT(m_hasColElements);
if (!m_columnRenderersValid)
updateColumnCache();
unsigned columnCount = 0;
for (unsigned i = 0; i < m_columnRenderers.size(); i++) {
RenderTableCol* columnRenderer = m_columnRenderers[i];
unsigned span = columnRenderer->span();
unsigned startCol = columnCount;
ASSERT(span >= 1);
unsigned endCol = columnCount + span - 1;
columnCount += span;
if (columnCount > col) {
if (startEdge)
*startEdge = startCol == col;
if (endEdge)
*endEdge = endCol == col;
return columnRenderer;
}
}
return 0;
}
void RenderTable::recalcSections() const
{
ASSERT(m_needsSectionRecalc);
m_head = 0;
m_foot = 0;
m_firstBody = 0;
m_hasColElements = false;
m_hasCellColspanThatDeterminesTableWidth = hasCellColspanThatDeterminesTableWidth();
RenderObject* nextSibling;
for (RenderObject* child = firstChild(); child; child = nextSibling) {
nextSibling = child->nextSibling();
switch (child->style()->display()) {
case TABLE_COLUMN:
case TABLE_COLUMN_GROUP:
m_hasColElements = true;
break;
case TABLE_HEADER_GROUP:
if (child->isTableSection()) {
RenderTableSection* section = toRenderTableSection(child);
if (!m_head)
m_head = section;
else if (!m_firstBody)
m_firstBody = section;
section->recalcCellsIfNeeded();
}
break;
case TABLE_FOOTER_GROUP:
if (child->isTableSection()) {
RenderTableSection* section = toRenderTableSection(child);
if (!m_foot)
m_foot = section;
else if (!m_firstBody)
m_firstBody = section;
section->recalcCellsIfNeeded();
}
break;
case TABLE_ROW_GROUP:
if (child->isTableSection()) {
RenderTableSection* section = toRenderTableSection(child);
if (!m_firstBody)
m_firstBody = section;
section->recalcCellsIfNeeded();
}
break;
default:
break;
}
}
unsigned maxCols = 0;
for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
if (child->isTableSection()) {
RenderTableSection* section = toRenderTableSection(child);
unsigned sectionCols = section->numColumns();
if (sectionCols > maxCols)
maxCols = sectionCols;
}
}
m_columns.resize(maxCols);
m_columnPos.resize(maxCols + 1);
ASSERT(selfNeedsLayout());
m_needsSectionRecalc = false;
}
int RenderTable::calcBorderStart() const
{
if (!collapseBorders())
return RenderBlock::borderStart();
if (!numEffCols())
return 0;
unsigned borderWidth = 0;
const BorderValue& tableStartBorder = style()->borderStart();
if (tableStartBorder.style() == BHIDDEN)
return 0;
if (tableStartBorder.style() > BHIDDEN)
borderWidth = tableStartBorder.width();
if (RenderTableCol* column = colElement(0)) {
const BorderValue& columnAdjoiningBorder = column->style()->borderStart();
if (columnAdjoiningBorder.style() == BHIDDEN)
return 0;
if (columnAdjoiningBorder.style() > BHIDDEN)
borderWidth = max(borderWidth, columnAdjoiningBorder.width());
}
if (const RenderTableSection* topNonEmptySection = this->topNonEmptySection()) {
const BorderValue& sectionAdjoiningBorder = topNonEmptySection->borderAdjoiningTableStart();
if (sectionAdjoiningBorder.style() == BHIDDEN)
return 0;
if (sectionAdjoiningBorder.style() > BHIDDEN)
borderWidth = max(borderWidth, sectionAdjoiningBorder.width());
if (const RenderTableCell* adjoiningStartCell = topNonEmptySection->firstRowCellAdjoiningTableStart()) {
const BorderValue& startCellAdjoiningBorder = adjoiningStartCell->borderAdjoiningTableStart();
if (startCellAdjoiningBorder.style() == BHIDDEN)
return 0;
const BorderValue& firstRowAdjoiningBorder = adjoiningStartCell->row()->borderAdjoiningTableStart();
if (firstRowAdjoiningBorder.style() == BHIDDEN)
return 0;
if (startCellAdjoiningBorder.style() > BHIDDEN)
borderWidth = max(borderWidth, startCellAdjoiningBorder.width());
if (firstRowAdjoiningBorder.style() > BHIDDEN)
borderWidth = max(borderWidth, firstRowAdjoiningBorder.width());
}
}
return (borderWidth + (style()->isLeftToRightDirection() ? 0 : 1)) / 2;
}
int RenderTable::calcBorderEnd() const
{
if (!collapseBorders())
return RenderBlock::borderEnd();
if (!numEffCols())
return 0;
unsigned borderWidth = 0;
const BorderValue& tableEndBorder = style()->borderEnd();
if (tableEndBorder.style() == BHIDDEN)
return 0;
if (tableEndBorder.style() > BHIDDEN)
borderWidth = tableEndBorder.width();
unsigned endColumn = numEffCols() - 1;
if (RenderTableCol* column = colElement(endColumn)) {
const BorderValue& columnAdjoiningBorder = column->style()->borderEnd();
if (columnAdjoiningBorder.style() == BHIDDEN)
return 0;
if (columnAdjoiningBorder.style() > BHIDDEN)
borderWidth = max(borderWidth, columnAdjoiningBorder.width());
}
if (const RenderTableSection* topNonEmptySection = this->topNonEmptySection()) {
const BorderValue& sectionAdjoiningBorder = topNonEmptySection->borderAdjoiningTableEnd();
if (sectionAdjoiningBorder.style() == BHIDDEN)
return 0;
if (sectionAdjoiningBorder.style() > BHIDDEN)
borderWidth = max(borderWidth, sectionAdjoiningBorder.width());
if (const RenderTableCell* adjoiningEndCell = topNonEmptySection->firstRowCellAdjoiningTableEnd()) {
const BorderValue& endCellAdjoiningBorder = adjoiningEndCell->borderAdjoiningTableEnd();
if (endCellAdjoiningBorder.style() == BHIDDEN)
return 0;
const BorderValue& firstRowAdjoiningBorder = adjoiningEndCell->row()->borderAdjoiningTableEnd();
if (firstRowAdjoiningBorder.style() == BHIDDEN)
return 0;
if (endCellAdjoiningBorder.style() > BHIDDEN)
borderWidth = max(borderWidth, endCellAdjoiningBorder.width());
if (firstRowAdjoiningBorder.style() > BHIDDEN)
borderWidth = max(borderWidth, firstRowAdjoiningBorder.width());
}
}
return (borderWidth + (style()->isLeftToRightDirection() ? 1 : 0)) / 2;
}
void RenderTable::recalcBordersInRowDirection()
{
m_borderStart = calcBorderStart();
m_borderEnd = calcBorderEnd();
}
int RenderTable::borderBefore() const
{
if (collapseBorders()) {
recalcSectionsIfNeeded();
return outerBorderBefore();
}
return RenderBlock::borderBefore();
}
int RenderTable::borderAfter() const
{
if (collapseBorders()) {
recalcSectionsIfNeeded();
return outerBorderAfter();
}
return RenderBlock::borderAfter();
}
int RenderTable::outerBorderBefore() const
{
if (!collapseBorders())
return 0;
int borderWidth = 0;
if (RenderTableSection* topSection = this->topSection()) {
borderWidth = topSection->outerBorderBefore();
if (borderWidth < 0)
return 0;
}
const BorderValue& tb = style()->borderBefore();
if (tb.style() == BHIDDEN)
return 0;
if (tb.style() > BHIDDEN)
borderWidth = max<int>(borderWidth, tb.width() / 2);
return borderWidth;
}
int RenderTable::outerBorderAfter() const
{
if (!collapseBorders())
return 0;
int borderWidth = 0;
if (RenderTableSection* section = bottomSection()) {
borderWidth = section->outerBorderAfter();
if (borderWidth < 0)
return 0;
}
const BorderValue& tb = style()->borderAfter();
if (tb.style() == BHIDDEN)
return 0;
if (tb.style() > BHIDDEN)
borderWidth = max<int>(borderWidth, (tb.width() + 1) / 2);
return borderWidth;
}
int RenderTable::outerBorderStart() const
{
if (!collapseBorders())
return 0;
int borderWidth = 0;
const BorderValue& tb = style()->borderStart();
if (tb.style() == BHIDDEN)
return 0;
if (tb.style() > BHIDDEN)
borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 0 : 1)) / 2;
bool allHidden = true;
for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) {
int sw = section->outerBorderStart();
if (sw < 0)
continue;
allHidden = false;
borderWidth = max(borderWidth, sw);
}
if (allHidden)
return 0;
return borderWidth;
}
int RenderTable::outerBorderEnd() const
{
if (!collapseBorders())
return 0;
int borderWidth = 0;
const BorderValue& tb = style()->borderEnd();
if (tb.style() == BHIDDEN)
return 0;
if (tb.style() > BHIDDEN)
borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 1 : 0)) / 2;
bool allHidden = true;
for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) {
int sw = section->outerBorderEnd();
if (sw < 0)
continue;
allHidden = false;
borderWidth = max(borderWidth, sw);
}
if (allHidden)
return 0;
return borderWidth;
}
RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const
{
recalcSectionsIfNeeded();
if (section == m_head)
return 0;
RenderObject* prevSection = section == m_foot ? lastChild() : section->previousSibling();
while (prevSection) {
if (prevSection->isTableSection() && prevSection != m_head && prevSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || toRenderTableSection(prevSection)->numRows()))
break;
prevSection = prevSection->previousSibling();
}
if (!prevSection && m_head && (skipEmptySections == DoNotSkipEmptySections || m_head->numRows()))
prevSection = m_head;
return toRenderTableSection(prevSection);
}
RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const
{
recalcSectionsIfNeeded();
if (section == m_foot)
return 0;
RenderObject* nextSection = section == m_head ? firstChild() : section->nextSibling();
while (nextSection) {
if (nextSection->isTableSection() && nextSection != m_head && nextSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || toRenderTableSection(nextSection)->numRows()))
break;
nextSection = nextSection->nextSibling();
}
if (!nextSection && m_foot && (skipEmptySections == DoNotSkipEmptySections || m_foot->numRows()))
nextSection = m_foot;
return toRenderTableSection(nextSection);
}
RenderTableSection* RenderTable::bottomSection() const
{
recalcSectionsIfNeeded();
if (m_foot)
return m_foot;
for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
if (child->isTableSection())
return toRenderTableSection(child);
}
return 0;
}
RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
{
recalcSectionsIfNeeded();
unsigned r = cell->rowIndex();
RenderTableSection* section = 0;
unsigned rAbove = 0;
if (r > 0) {
section = cell->section();
rAbove = r - 1;
} else {
section = sectionAbove(cell->section(), SkipEmptySections);
if (section) {
ASSERT(section->numRows());
rAbove = section->numRows() - 1;
}
}
if (section) {
unsigned effCol = colToEffCol(cell->col());
RenderTableSection::CellStruct& aboveCell = section->cellAt(rAbove, effCol);
return aboveCell.primaryCell();
} else
return 0;
}
RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const
{
recalcSectionsIfNeeded();
unsigned r = cell->rowIndex() + cell->rowSpan() - 1;
RenderTableSection* section = 0;
unsigned rBelow = 0;
if (r < cell->section()->numRows() - 1) {
section = cell->section();
rBelow = r + 1;
} else {
section = sectionBelow(cell->section(), SkipEmptySections);
if (section)
rBelow = 0;
}
if (section) {
unsigned effCol = colToEffCol(cell->col());
RenderTableSection::CellStruct& belowCell = section->cellAt(rBelow, effCol);
return belowCell.primaryCell();
} else
return 0;
}
RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const
{
recalcSectionsIfNeeded();
RenderTableSection* section = cell->section();
unsigned effCol = colToEffCol(cell->col());
if (!effCol)
return 0;
RenderTableSection::CellStruct& prevCell = section->cellAt(cell->rowIndex(), effCol - 1);
return prevCell.primaryCell();
}
RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const
{
recalcSectionsIfNeeded();
unsigned effCol = colToEffCol(cell->col() + cell->colSpan());
if (effCol >= numEffCols())
return 0;
return cell->section()->primaryCellAt(cell->rowIndex(), effCol);
}
RenderBlock* RenderTable::firstLineBlock() const
{
return 0;
}
void RenderTable::updateFirstLetter()
{
}
int RenderTable::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
{
ASSERT(linePositionMode == PositionOnContainingLine);
int baseline = firstLineBoxBaseline();
if (baseline != -1) {
if (isInline())
return beforeMarginInLineDirection(direction) + baseline;
return baseline;
}
return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
}
int RenderTable::inlineBlockBaseline(LineDirectionMode) const
{
return -1;
}
int RenderTable::firstLineBoxBaseline() const
{
if (isWritingModeRoot())
return -1;
recalcSectionsIfNeeded();
const RenderTableSection* topNonEmptySection = this->topNonEmptySection();
if (!topNonEmptySection)
return -1;
int baseline = topNonEmptySection->firstLineBoxBaseline();
if (baseline > 0)
return topNonEmptySection->logicalTop() + baseline;
return -1;
}
LayoutRect RenderTable::overflowClipRect(const LayoutPoint& location, OverlayScrollbarSizeRelevancy relevancy)
{
LayoutRect rect = RenderBlock::overflowClipRect(location, relevancy);
if (!m_captions.isEmpty()) {
if (style()->isHorizontalWritingMode()) {
rect.setHeight(height());
rect.setY(location.y());
} else {
rect.setWidth(width());
rect.setX(location.x());
}
}
return rect;
}
bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
{
LayoutPoint adjustedLocation = accumulatedOffset + location();
if (!hasOverflowClip() || locationInContainer.intersects(overflowClipRect(adjustedLocation))) {
for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) {
LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), adjustedLocation);
if (child->nodeAtPoint(request, result, locationInContainer, childPoint, action)) {
updateHitTestResult(result, toLayoutPoint(locationInContainer.point() - childPoint));
return true;
}
}
}
}
LayoutRect boundsRect(adjustedLocation, size());
if (visibleToHitTestRequest(request) && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && locationInContainer.intersects(boundsRect)) {
updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(adjustedLocation)));
if (!result.addNodeToRectBasedTestResult(node(), request, locationInContainer, boundsRect))
return true;
}
return false;
}
RenderTable* RenderTable::createAnonymousWithParentRenderer(const RenderObject* parent)
{
RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE);
RenderTable* newTable = new RenderTable(0);
newTable->setDocumentForAnonymous(&parent->document());
newTable->setStyle(newStyle.release());
return newTable;
}
const BorderValue& RenderTable::tableStartBorderAdjoiningCell(const RenderTableCell* cell) const
{
ASSERT(cell->isFirstOrLastCellInRow());
if (hasSameDirectionAs(cell->row()))
return style()->borderStart();
return style()->borderEnd();
}
const BorderValue& RenderTable::tableEndBorderAdjoiningCell(const RenderTableCell* cell) const
{
ASSERT(cell->isFirstOrLastCellInRow());
if (hasSameDirectionAs(cell->row()))
return style()->borderEnd();
return style()->borderStart();
}
}