diff options
author | Konstantin Tokarev <annulen@yandex.ru> | 2016-08-25 19:20:41 +0300 |
---|---|---|
committer | Konstantin Tokarev <annulen@yandex.ru> | 2017-02-02 12:30:55 +0000 |
commit | 6882a04fb36642862b11efe514251d32070c3d65 (patch) | |
tree | b7959826000b061fd5ccc7512035c7478742f7b0 /Source/WebCore/rendering/RenderTable.cpp | |
parent | ab6df191029eeeb0b0f16f127d553265659f739e (diff) | |
download | qtwebkit-6882a04fb36642862b11efe514251d32070c3d65.tar.gz |
Imported QtWebKit TP3 (git b57bc6801f1876c3220d5a4bfea33d620d477443)
Change-Id: I3b1d8a2808782c9f34d50240000e20cb38d3680f
Reviewed-by: Konstantin Tokarev <annulen@yandex.ru>
Diffstat (limited to 'Source/WebCore/rendering/RenderTable.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderTable.cpp | 721 |
1 files changed, 432 insertions, 289 deletions
diff --git a/Source/WebCore/rendering/RenderTable.cpp b/Source/WebCore/rendering/RenderTable.cpp index 336b6c995..05325314a 100644 --- a/Source/WebCore/rendering/RenderTable.cpp +++ b/Source/WebCore/rendering/RenderTable.cpp @@ -4,7 +4,7 @@ * (C) 1998 Waldo Bastian (bastian@kde.org) * (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2014 Apple Inc. All rights reserved. * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) * * This library is free software; you can redistribute it and/or @@ -35,7 +35,9 @@ #include "HTMLNames.h" #include "HTMLTableElement.h" #include "LayoutRepainter.h" +#include "RenderIterator.h" #include "RenderLayer.h" +#include "RenderNamedFlowFragment.h" #include "RenderTableCaption.h" #include "RenderTableCell.h" #include "RenderTableCol.h" @@ -44,23 +46,47 @@ #include "StyleInheritedData.h" #include <wtf/StackStats.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) +RenderTable::RenderTable(Element& element, Ref<RenderStyle>&& style) + : RenderBlock(element, WTFMove(style), 0) + , m_head(nullptr) + , m_foot(nullptr) + , m_firstBody(nullptr) + , m_currentBorder(nullptr) , m_collapsedBordersValid(false) + , m_collapsedEmptyBorderIsPresent(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) + , m_columnOffsetTop(-1) + , m_columnOffsetHeight(-1) +{ + setChildrenInline(false); + m_columnPos.fill(0, 1); +} + +RenderTable::RenderTable(Document& document, Ref<RenderStyle>&& style) + : RenderBlock(document, WTFMove(style), 0) + , m_head(nullptr) + , m_foot(nullptr) + , m_firstBody(nullptr) + , m_currentBorder(nullptr) + , m_collapsedBordersValid(false) + , m_collapsedEmptyBorderIsPresent(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) @@ -68,7 +94,6 @@ RenderTable::RenderTable(Element* element) { setChildrenInline(false); m_columnPos.fill(0, 1); - } RenderTable::~RenderTable() @@ -78,26 +103,26 @@ RenderTable::~RenderTable() void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); - propagateStyleToAnonymousChildren(); + propagateStyleToAnonymousChildren(PropagateToAllChildren); ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO; // In the collapsed border model, there is no cell spacing. - m_hSpacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing(); - m_vSpacing = collapseBorders() ? 0 : style()->verticalBorderSpacing(); + 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 || style().tableLayout() != oldTableLayout) { // According to the CSS2 spec, you only use fixed table layout if an // explicit width is specified on the table. Auto width implies auto table layout. - if (style()->tableLayout() == TFIXED && !style()->logicalWidth().isAuto()) - m_tableLayout = adoptPtr(new FixedTableLayout(this)); + if (style().tableLayout() == TFIXED && !style().logicalWidth().isAuto()) + m_tableLayout = std::make_unique<FixedTableLayout>(this); else - m_tableLayout = adoptPtr(new AutoTableLayout(this)); + m_tableLayout = std::make_unique<AutoTableLayout>(this); } // If border was changed, invalidate collapsed borders cache. - if (!needsLayout() && oldStyle && oldStyle->border() != style()->border()) + if (oldStyle && oldStyle->border() != style().border()) invalidateCollapsedBorders(); } @@ -116,47 +141,47 @@ void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) { bool wrapInAnonymousSection = !child->isOutOfFlowPositioned(); - if (child->isTableCaption()) + if (is<RenderTableCaption>(*child)) wrapInAnonymousSection = false; - else if (child->isRenderTableCol()) { + else if (is<RenderTableCol>(*child)) { m_hasColElements = true; wrapInAnonymousSection = false; - } else if (child->isTableSection()) { - switch (child->style()->display()) { + } else if (is<RenderTableSection>(*child)) { + switch (child->style().display()) { case TABLE_HEADER_GROUP: resetSectionPointerIfNotBefore(m_head, beforeChild); if (!m_head) { - m_head = toRenderTableSection(child); + m_head = downcast<RenderTableSection>(child); } else { resetSectionPointerIfNotBefore(m_firstBody, beforeChild); if (!m_firstBody) - m_firstBody = toRenderTableSection(child); + m_firstBody = downcast<RenderTableSection>(child); } wrapInAnonymousSection = false; break; case TABLE_FOOTER_GROUP: resetSectionPointerIfNotBefore(m_foot, beforeChild); if (!m_foot) { - m_foot = toRenderTableSection(child); + m_foot = downcast<RenderTableSection>(child); wrapInAnonymousSection = false; break; } - // Fall through. + FALLTHROUGH; case TABLE_ROW_GROUP: resetSectionPointerIfNotBefore(m_firstBody, beforeChild); if (!m_firstBody) - m_firstBody = toRenderTableSection(child); + m_firstBody = downcast<RenderTableSection>(child); wrapInAnonymousSection = false; break; default: ASSERT_NOT_REACHED(); } - } else if (child->isTableCell() || child->isTableRow()) + } else if (is<RenderTableCell>(*child) || is<RenderTableRow>(*child)) wrapInAnonymousSection = true; else wrapInAnonymousSection = true; - if (child->isTableSection()) + if (is<RenderTableSection>(*child)) setNeedsSectionRecalc(); if (!wrapInAnonymousSection) { @@ -167,31 +192,32 @@ void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) return; } - if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous() && !lastChild()->isBeforeContent()) { - lastChild()->addChild(child); + if (!beforeChild && is<RenderTableSection>(lastChild()) && lastChild()->isAnonymous() && !lastChild()->isBeforeContent()) { + downcast<RenderTableSection>(*lastChild()).addChild(child); return; } if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) { RenderObject* section = beforeChild->previousSibling(); - if (section && section->isTableSection() && section->isAnonymous()) { - section->addChild(child); + if (is<RenderTableSection>(section) && section->isAnonymous()) { + downcast<RenderTableSection>(*section).addChild(child); return; } } RenderObject* lastBox = beforeChild; - while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION && lastBox->style()->display() != TABLE_COLUMN_GROUP) + while (lastBox && lastBox->parent()->isAnonymous() && !is<RenderTableSection>(*lastBox) && lastBox->style().display() != TABLE_CAPTION && lastBox->style().display() != TABLE_COLUMN_GROUP) lastBox = lastBox->parent(); - if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox)) { - if (beforeChild == lastBox) - beforeChild = lastBox->firstChild(); - lastBox->addChild(child, beforeChild); + if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox) && lastBox->isTableSection()) { + RenderTableSection& section = downcast<RenderTableSection>(*lastBox); + if (beforeChild == §ion) + beforeChild = section.firstRow(); + section.addChild(child, beforeChild); return; } - if (beforeChild && !beforeChild->isTableSection() && beforeChild->style()->display() != TABLE_CAPTION && beforeChild->style()->display() != TABLE_COLUMN_GROUP) - beforeChild = 0; + if (beforeChild && !is<RenderTableSection>(*beforeChild) && beforeChild->style().display() != TABLE_CAPTION && beforeChild->style().display() != TABLE_COLUMN_GROUP) + beforeChild = nullptr; RenderTableSection* section = RenderTableSection::createAnonymousWithParentRenderer(this); addChild(section, beforeChild); @@ -206,18 +232,21 @@ void RenderTable::addCaption(const RenderTableCaption* caption) void RenderTable::removeCaption(const RenderTableCaption* oldCaption) { - size_t index = m_captions.find(oldCaption); - ASSERT(index != notFound); - if (index == notFound) - return; - - m_captions.remove(index); + bool removed = m_captions.removeFirst(oldCaption); + ASSERT_UNUSED(removed, removed); } void RenderTable::invalidateCachedColumns() { m_columnRenderersValid = false; m_columnRenderers.resize(0); + m_effectiveColumnIndexMap.clear(); +} + +void RenderTable::invalidateCachedColumnOffsets() +{ + m_columnOffsetTop = -1; + m_columnOffsetHeight = -1; } void RenderTable::addColumn(const RenderTableCol*) @@ -248,48 +277,47 @@ void RenderTable::updateLogicalWidth() } RenderBlock* cb = containingBlock(); - RenderView* renderView = view(); LayoutUnit availableLogicalWidth = containingBlockLogicalWidthForContent(); - bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode(); + bool hasPerpendicularContainingBlock = cb->style().isHorizontalWritingMode() != style().isHorizontalWritingMode(); LayoutUnit containerWidthInInlineDirection = hasPerpendicularContainingBlock ? perpendicularContainingBlockLogicalHeight() : availableLogicalWidth; - Length styleLogicalWidth = style()->logicalWidth(); + Length styleLogicalWidth = style().logicalWidth(); if ((styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive()) || styleLogicalWidth.isIntrinsic()) setLogicalWidth(convertStyleLogicalWidthToComputedWidth(styleLogicalWidth, containerWidthInInlineDirection)); else { // Subtract out any fixed margins from our available width for auto width tables. - LayoutUnit marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth, renderView); - LayoutUnit marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth, renderView); + LayoutUnit marginStart = minimumValueForLength(style().marginStart(), availableLogicalWidth); + LayoutUnit marginEnd = minimumValueForLength(style().marginEnd(), availableLogicalWidth); LayoutUnit marginTotal = marginStart + marginEnd; // Subtract out our margins to get the available content width. - LayoutUnit availableContentLogicalWidth = max<LayoutUnit>(0, containerWidthInInlineDirection - marginTotal); + LayoutUnit availableContentLogicalWidth = std::max<LayoutUnit>(0, containerWidthInInlineDirection - marginTotal); if (shrinkToAvoidFloats() && cb->containsFloats() && !hasPerpendicularContainingBlock) { // FIXME: Work with regions someday. availableContentLogicalWidth = shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, cb, 0); } // Ensure we aren't bigger than our available width. - setLogicalWidth(min<int>(availableContentLogicalWidth, maxPreferredLogicalWidth())); + setLogicalWidth(std::min(availableContentLogicalWidth, maxPreferredLogicalWidth())); } // Ensure we aren't smaller than our min preferred width. - setLogicalWidth(max<int>(logicalWidth(), minPreferredLogicalWidth())); + setLogicalWidth(std::max(logicalWidth(), minPreferredLogicalWidth())); // Ensure we aren't bigger than our max-width style. - Length styleMaxLogicalWidth = style()->logicalMaxWidth(); + Length styleMaxLogicalWidth = style().logicalMaxWidth(); if ((styleMaxLogicalWidth.isSpecified() && !styleMaxLogicalWidth.isNegative()) || styleMaxLogicalWidth.isIntrinsic()) { LayoutUnit computedMaxLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMaxLogicalWidth, availableLogicalWidth); - setLogicalWidth(min<int>(logicalWidth(), computedMaxLogicalWidth)); + setLogicalWidth(std::min(logicalWidth(), computedMaxLogicalWidth)); } // Ensure we aren't smaller than our min-width style. - Length styleMinLogicalWidth = style()->logicalMinWidth(); + Length styleMinLogicalWidth = style().logicalMinWidth(); if ((styleMinLogicalWidth.isSpecified() && !styleMinLogicalWidth.isNegative()) || styleMinLogicalWidth.isIntrinsic()) { LayoutUnit computedMinLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMinLogicalWidth, availableLogicalWidth); - setLogicalWidth(max<int>(logicalWidth(), computedMinLogicalWidth)); + setLogicalWidth(std::max(logicalWidth(), computedMinLogicalWidth)); } // Finally, with our true width determined, compute our margins for real. @@ -300,15 +328,15 @@ void RenderTable::updateLogicalWidth() if (avoidsFloats() && cb->containsFloats()) containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidthInRegion(0); // FIXME: Work with regions someday. ComputedMarginValues marginValues; - bool hasInvertedDirection = cb->style()->isLeftToRightDirection() == style()->isLeftToRightDirection(); + bool hasInvertedDirection = cb->style().isLeftToRightDirection() == style().isLeftToRightDirection(); computeInlineDirectionMargins(cb, containerLogicalWidthForAutoMargins, 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, renderView)); - setMarginEnd(minimumValueForLength(style()->marginEnd(), availableLogicalWidth, renderView)); + setMarginStart(minimumValueForLength(style().marginStart(), availableLogicalWidth)); + setMarginEnd(minimumValueForLength(style().marginEnd(), availableLogicalWidth)); } } @@ -320,33 +348,33 @@ LayoutUnit RenderTable::convertStyleLogicalWidthToComputedWidth(const Length& st // HTML tables' width styles already include borders and paddings, but CSS tables' width styles do not. LayoutUnit borders = 0; - bool isCSSTable = !node() || !isHTMLTableElement(node()); - if (isCSSTable && styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive() && style()->boxSizing() == CONTENT_BOX) + bool isCSSTable = !is<HTMLTableElement>(element()); + if (isCSSTable && styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive() && style().boxSizing() == CONTENT_BOX) borders = borderStart() + borderEnd() + (collapseBorders() ? LayoutUnit() : paddingStart() + paddingEnd()); - return minimumValueForLength(styleLogicalWidth, availableWidth, view()) + borders; + return minimumValueForLength(styleLogicalWidth, availableWidth) + borders; } LayoutUnit RenderTable::convertStyleLogicalHeightToComputedHeight(const Length& styleLogicalHeight) { - LayoutUnit computedLogicalHeight = 0; + LayoutUnit borderAndPaddingBefore = borderBefore() + (collapseBorders() ? LayoutUnit() : paddingBefore()); + LayoutUnit borderAndPaddingAfter = borderAfter() + (collapseBorders() ? LayoutUnit() : paddingAfter()); + LayoutUnit borderAndPadding = borderAndPaddingBefore + borderAndPaddingAfter; if (styleLogicalHeight.isFixed()) { // HTML tables size as though CSS height includes border/padding, CSS tables do not. LayoutUnit borders = LayoutUnit(); // FIXME: We cannot apply box-sizing: content-box on <table> which other browsers allow. - if ((node() && isHTMLTableElement(node())) || style()->boxSizing() == BORDER_BOX) { - LayoutUnit borderAndPaddingBefore = borderBefore() + (collapseBorders() ? LayoutUnit() : paddingBefore()); - LayoutUnit borderAndPaddingAfter = borderAfter() + (collapseBorders() ? LayoutUnit() : paddingAfter()); - borders = borderAndPaddingBefore + borderAndPaddingAfter; + if (is<HTMLTableElement>(element()) || style().boxSizing() == BORDER_BOX) { + borders = borderAndPadding; } - computedLogicalHeight = styleLogicalHeight.value() - borders; - } else if (styleLogicalHeight.isPercent()) - computedLogicalHeight = computePercentageLogicalHeight(styleLogicalHeight); - else if (styleLogicalHeight.isViewportPercentage()) - computedLogicalHeight = minimumValueForLength(styleLogicalHeight, 0, view()); + return styleLogicalHeight.value() - borders; + } else if (styleLogicalHeight.isPercentOrCalculated()) + return computePercentageLogicalHeight(styleLogicalHeight).valueOr(0); + else if (styleLogicalHeight.isIntrinsic()) + return computeIntrinsicLogicalContentHeightUsing(styleLogicalHeight, logicalHeight() - borderAndPadding, borderAndPadding).valueOr(0); else ASSERT_NOT_REACHED(); - return max<LayoutUnit>(0, computedLogicalHeight); + return LayoutUnit(); } void RenderTable::layoutCaption(RenderTableCaption* caption) @@ -369,7 +397,7 @@ void RenderTable::layoutCaption(RenderTableCaption* caption) setLogicalHeight(logicalHeight() + caption->logicalHeight() + caption->marginBefore() + caption->marginAfter()); } -void RenderTable::distributeExtraLogicalHeight(int extraLogicalHeight) +void RenderTable::distributeExtraLogicalHeight(LayoutUnit extraLogicalHeight) { if (extraLogicalHeight <= 0) return; @@ -403,20 +431,18 @@ void RenderTable::layout() // FIXME: We should do this recalc lazily in borderStart/borderEnd so that we don't have to make sure // to call this before we call borderStart/borderEnd to avoid getting a stale value. recalcBordersInRowDirection(); - - LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); - LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - setLogicalHeight(0); + LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); + LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode()); - initMaxMarginValues(); - LayoutUnit oldLogicalWidth = logicalWidth(); + LayoutUnit oldLogicalHeight = logicalHeight(); + setLogicalHeight(0); updateLogicalWidth(); if (logicalWidth() != oldLogicalWidth) { for (unsigned i = 0; i < m_captions.size(); i++) - m_captions[i]->setNeedsLayout(true, MarkOnlyThis); + m_captions[i]->setNeedsLayout(MarkOnlyThis); } // FIXME: The optimisation below doesn't work since the internal table // layout could have changed. we need to add a flag to the table @@ -432,19 +458,19 @@ void RenderTable::layout() bool collapsing = collapseBorders(); - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (child->isTableSection()) { - RenderTableSection* section = toRenderTableSection(child); + for (auto& child : childrenOfType<RenderElement>(*this)) { + if (is<RenderTableSection>(child)) { + RenderTableSection& section = downcast<RenderTableSection>(child); if (m_columnLogicalWidthChanged) - section->setChildNeedsLayout(true, MarkOnlyThis); - section->layoutIfNeeded(); - totalSectionLogicalHeight += section->calcRowLogicalHeight(); + section.setChildNeedsLayout(MarkOnlyThis); + section.layoutIfNeeded(); + totalSectionLogicalHeight += section.calcRowLogicalHeight(); if (collapsing) - section->recalcOuterBorder(); - ASSERT(!section->needsLayout()); - } else if (child->isRenderTableCol()) { - child->layoutIfNeeded(); - ASSERT(!child->needsLayout()); + section.recalcOuterBorder(); + ASSERT(!section.needsLayout()); + } else if (is<RenderTableCol>(child)) { + downcast<RenderTableCol>(child).layoutIfNeeded(); + ASSERT(!child.needsLayout()); } } @@ -457,13 +483,13 @@ void RenderTable::layout() // FIXME: Collapse caption margin. if (!m_captions.isEmpty()) { for (unsigned i = 0; i < m_captions.size(); i++) { - if (m_captions[i]->style()->captionSide() == CAPBOTTOM) + if (m_captions[i]->style().captionSide() == CAPBOTTOM) continue; layoutCaption(m_captions[i]); } if (logicalHeight() != oldTableLogicalTop) { sectionMoved = true; - movedSectionLogicalTop = min(logicalHeight(), oldTableLogicalTop); + movedSectionLogicalTop = std::min(logicalHeight(), oldTableLogicalTop); } } @@ -477,43 +503,43 @@ void RenderTable::layout() LayoutUnit computedLogicalHeight = 0; - Length logicalHeightLength = style()->logicalHeight(); - if (logicalHeightLength.isSpecified() && logicalHeightLength.isPositive()) + Length logicalHeightLength = style().logicalHeight(); + if (logicalHeightLength.isIntrinsic() || (logicalHeightLength.isSpecified() && logicalHeightLength.isPositive())) computedLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalHeightLength); - Length logicalMaxHeightLength = style()->logicalMaxHeight(); - if (logicalMaxHeightLength.isSpecified() && !logicalMaxHeightLength.isNegative()) { + Length logicalMaxHeightLength = style().logicalMaxHeight(); + if (logicalMaxHeightLength.isIntrinsic() || (logicalMaxHeightLength.isSpecified() && !logicalMaxHeightLength.isNegative())) { LayoutUnit computedMaxLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMaxHeightLength); - computedLogicalHeight = min(computedLogicalHeight, computedMaxLogicalHeight); + computedLogicalHeight = std::min(computedLogicalHeight, computedMaxLogicalHeight); } - Length logicalMinHeightLength = style()->logicalMinHeight(); - if (logicalMinHeightLength.isSpecified() && !logicalMinHeightLength.isNegative()) { + Length logicalMinHeightLength = style().logicalMinHeight(); + if (logicalMinHeightLength.isIntrinsic() || (logicalMinHeightLength.isSpecified() && !logicalMinHeightLength.isNegative())) { LayoutUnit computedMinLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMinHeightLength); - computedLogicalHeight = max(computedLogicalHeight, computedMinLogicalHeight); + computedLogicalHeight = std::max(computedLogicalHeight, computedMinLogicalHeight); } - distributeExtraLogicalHeight(floorToInt(computedLogicalHeight - totalSectionLogicalHeight)); + distributeExtraLogicalHeight(computedLogicalHeight - totalSectionLogicalHeight); for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) section->layoutRows(); - if (!topSection() && computedLogicalHeight > totalSectionLogicalHeight && !document()->inQuirksMode()) { + if (!topSection() && computedLogicalHeight > totalSectionLogicalHeight && !document().inQuirksMode()) { // Completely empty tables (with no sections or anything) should at least honor specified height // in strict mode. setLogicalHeight(logicalHeight() + computedLogicalHeight); } - LayoutUnit sectionLogicalLeft = style()->isLeftToRightDirection() ? borderStart() : borderEnd(); + LayoutUnit sectionLogicalLeft = style().isLeftToRightDirection() ? borderStart() : borderEnd(); if (!collapsing) - sectionLogicalLeft += style()->isLeftToRightDirection() ? paddingStart() : paddingEnd(); + sectionLogicalLeft += style().isLeftToRightDirection() ? paddingStart() : paddingEnd(); // position the table sections 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()); + movedSectionLogicalTop = std::min(logicalHeight(), section->logicalTop()) + (style().isHorizontalWritingMode() ? section->visualOverflowRect().y() : section->visualOverflowRect().x()); } section->setLogicalLocation(LayoutPoint(sectionLogicalLeft, logicalHeight())); @@ -524,7 +550,7 @@ void RenderTable::layout() setLogicalHeight(logicalHeight() + borderAndPaddingAfter); for (unsigned i = 0; i < m_captions.size(); i++) { - if (m_captions[i]->style()->captionSide() != CAPBOTTOM) + if (m_captions[i]->style().captionSide() != CAPBOTTOM) continue; layoutCaption(m_captions[i]); } @@ -533,32 +559,71 @@ void RenderTable::layout() updateLogicalHeight(); // table can be containing block of positioned elements. - // FIXME: Only pass true if width or height changed. - layoutPositionedObjects(true); + bool dimensionChanged = oldLogicalWidth != logicalWidth() || oldLogicalHeight != logicalHeight(); + layoutPositionedObjects(dimensionChanged); updateLayerTransform(); // Layout was changed, so probably borders too. invalidateCollapsedBorders(); + // The location or height of one or more sections may have changed. + invalidateCachedColumnOffsets(); + computeOverflow(clientLogicalBottom()); statePusher.pop(); - if (view()->layoutState()->pageLogicalHeight()) - setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(this, logicalTop())); + if (view().layoutState()->pageLogicalHeight()) + setPageLogicalOffset(view().layoutState()->pageLogicalOffset(this, logicalTop())); bool didFullRepaint = repainter.repaintAfterLayout(); // Repaint with our new bounds if they are different from our old bounds. if (!didFullRepaint && sectionMoved) { - if (style()->isHorizontalWritingMode()) + 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; - setNeedsLayout(false); + clearNeedsLayout(); +} + +void RenderTable::invalidateCollapsedBorders(RenderTableCell* cellWithStyleChange) +{ + m_collapsedBordersValid = false; + m_collapsedBorders.clear(); + + for (auto& section : childrenOfType<RenderTableSection>(*this)) + section.clearCachedCollapsedBorders(); + + if (!m_collapsedEmptyBorderIsPresent) + return; + + if (cellWithStyleChange) { + // It is enough to invalidate just the surrounding cells when cell border style changes. + cellWithStyleChange->invalidateHasEmptyCollapsedBorders(); + if (auto* below = cellBelow(cellWithStyleChange)) + below->invalidateHasEmptyCollapsedBorders(); + if (auto* above = cellAbove(cellWithStyleChange)) + above->invalidateHasEmptyCollapsedBorders(); + if (auto* before = cellBefore(cellWithStyleChange)) + before->invalidateHasEmptyCollapsedBorders(); + if (auto* after = cellAfter(cellWithStyleChange)) + after->invalidateHasEmptyCollapsedBorders(); + return; + } + + for (auto& section : childrenOfType<RenderTableSection>(*this)) { + for (auto* row = section.firstRow(); row; row = row->nextRow()) { + for (auto* cell = row->firstCell(); cell; cell = cell->nextCell()) { + ASSERT(cell->table() == this); + cell->invalidateHasEmptyCollapsedBorders(); + } + } + } + m_collapsedEmptyBorderIsPresent = false; } // Collect all the unique border values that we want to paint in a sorted list. @@ -566,38 +631,31 @@ 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); + for (auto& section : childrenOfType<RenderTableSection>(*this)) { + for (RenderTableRow* row = section.firstRow(); row; row = row->nextRow()) { + for (RenderTableCell* cell = row->firstCell(); cell; cell = cell->nextCell()) { + ASSERT(cell->table() == this); + cell->collectBorderValues(m_collapsedBorders); } } } RenderTableCell::sortBorderValues(m_collapsedBorders); + m_collapsedBordersValid = true; } - void RenderTable::addOverflowFromChildren() { // Add overflow from borders. // Technically it's odd that we are incorporating the borders into layout overflow, which is only supposed to be about overflow from our // descendant objects, but since tables don't support overflow:auto, this works out fine. 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()) { + LayoutUnit rightBorderOverflow = width() + outerBorderRight() - borderRight(); + LayoutUnit leftBorderOverflow = borderLeft() - outerBorderLeft(); + LayoutUnit bottomBorderOverflow = height() + outerBorderBottom() - borderBottom(); + LayoutUnit topBorderOverflow = borderTop() - outerBorderTop(); + LayoutRect borderOverflowRect(leftBorderOverflow, topBorderOverflow, rightBorderOverflow - leftBorderOverflow, bottomBorderOverflow - topBorderOverflow); + if (borderOverflowRect != borderBoxRect()) { addLayoutOverflow(borderOverflowRect); addVisualOverflow(borderOverflowRect); } @@ -618,10 +676,9 @@ void RenderTable::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) PaintPhase paintPhase = paintInfo.phase; - if (!isRoot()) { + if (!isDocumentElementRenderer()) { LayoutRect overflowBox = visualOverflowRect(); flipForWritingMode(overflowBox); - overflowBox.inflate(maximalOutlineSize(paintInfo.phase)); overflowBox.moveBy(adjustedPaintOffset); if (!overflowBox.intersects(paintInfo.rect)) return; @@ -636,7 +693,7 @@ void RenderTable::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) void RenderTable::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { PaintPhase paintPhase = paintInfo.phase; - if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE) + if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style().visibility() == VISIBLE) paintBoxDecorations(paintInfo, paintOffset); if (paintPhase == PaintPhaseMask) { @@ -656,14 +713,14 @@ void RenderTable::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs info.phase = paintPhase; info.updateSubtreePaintRootForChildren(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); + for (auto& box : childrenOfType<RenderBox>(*this)) { + if (!box.hasSelfPaintingLayer() && (box.isTableSection() || box.isTableCaption())) { + LayoutPoint childPoint = flipForWritingModeForChild(&box, paintOffset); + box.paint(info, childPoint); } } - if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) { + if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style().visibility() == VISIBLE) { recalcCollapsedBorders(); // Using our cached sorted styles, we then do individual passes, // painting each style of border from lowest precedence to highest precedence. @@ -680,7 +737,7 @@ void RenderTable::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs } // Paint outline. - if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE) + if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style().visibility() == VISIBLE) paintOutline(paintInfo, LayoutRect(paintOffset, size())); } @@ -688,8 +745,8 @@ 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()) { + bool captionIsBefore = (m_captions[i]->style().captionSide() != CAPBOTTOM) ^ style().isFlippedBlocksWritingMode(); + if (style().isHorizontalWritingMode()) { rect.setHeight(rect.height() - captionLogicalHeight); if (captionIsBefore) rect.move(0, captionLogicalHeight); @@ -703,25 +760,25 @@ void RenderTable::subtractCaptionRect(LayoutRect& rect) const void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { - if (!paintInfo.shouldPaintWithinRoot(this)) + if (!paintInfo.shouldPaintWithinRoot(*this)) return; LayoutRect rect(paintOffset, size()); subtractCaptionRect(rect); - BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context); - if (!boxShadowShouldBeAppliedToBackground(bleedAvoidance)) + BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context()); + if (!boxShadowShouldBeAppliedToBackground(rect.location(), bleedAvoidance)) paintBoxShadow(paintInfo, rect, style(), Normal); paintBackground(paintInfo, rect, bleedAvoidance); paintBoxShadow(paintInfo, rect, style(), Inset); - if (style()->hasBorder() && !collapseBorders()) + if (style().hasBorderDecoration() && !collapseBorders()) paintBorder(paintInfo, rect, style()); } void RenderTable::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { - if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) + if (style().visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; LayoutRect rect(paintOffset, size()); @@ -749,26 +806,26 @@ void RenderTable::computePreferredLogicalWidths() computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); - int bordersPaddingAndSpacing = bordersPaddingAndSpacingInRowDirection(); + LayoutUnit 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()); + m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, m_captions[i]->minPreferredLogicalWidth()); - RenderStyle* styleToUse = style(); - // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for min-width. - 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())); + RenderStyle& styleToUse = style(); + // FIXME: This should probably be checking for isSpecified since you should be able to use percentage or calc values for min-width. + 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())); } - // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for maxWidth. - if (styleToUse->logicalMaxWidth().isFixed()) { - m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); - m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); + // FIXME: This should probably be checking for isSpecified since you should be able to use percentage or calc values for maxWidth. + if (styleToUse.logicalMaxWidth().isFixed()) { + m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value())); + m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value())); } // FIXME: We should be adding borderAndPaddingLogicalWidth here, but m_tableLayout->computePreferredLogicalWidths already does, @@ -793,19 +850,14 @@ void RenderTable::splitColumn(unsigned position, unsigned firstSpan) // Propagate the change in our columns representation to the sections that don't need // cell recalc. If they do, they will be synced up directly with m_columns later. - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (!child->isTableSection()) - continue; - - RenderTableSection* section = toRenderTableSection(child); - if (section->needsCellRecalc()) + for (auto& section : childrenOfType<RenderTableSection>(*this)) { + if (section.needsCellRecalc()) continue; - section->splitColumn(position, firstSpan); + section.splitColumn(position, firstSpan); } m_columnPos.grow(numEffCols() + 1); - m_columnPos[numEffCols()] = m_columnPos[numEffCols() - 1]; } void RenderTable::appendColumn(unsigned span) @@ -813,51 +865,135 @@ void RenderTable::appendColumn(unsigned span) unsigned newColumnIndex = m_columns.size(); m_columns.append(ColumnStruct(span)); + // Unless the table has cell(s) with colspan that exceed the number of columns afforded + // by the other rows in the table we can use the fast path when mapping columns to effective columns. + m_hasCellColspanThatDeterminesTableWidth = m_hasCellColspanThatDeterminesTableWidth || span > 1; + // Propagate the change in our columns representation to the sections that don't need // cell recalc. If they do, they will be synced up directly with m_columns later. - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (!child->isTableSection()) + for (auto& section : childrenOfType<RenderTableSection>(*this)) { + if (section.needsCellRecalc()) continue; - RenderTableSection* section = toRenderTableSection(child); - if (section->needsCellRecalc()) - continue; - - section->appendColumn(newColumnIndex); + section.appendColumn(newColumnIndex); } m_columnPos.grow(numEffCols() + 1); - m_columnPos[numEffCols()] = m_columnPos[numEffCols() - 1]; } RenderTableCol* RenderTable::firstColumn() const { for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (child->isRenderTableCol()) - return toRenderTableCol(child); + if (is<RenderTableCol>(*child)) + return downcast<RenderTableCol>(child); // We allow only table-captions before columns or column-groups. - if (!child->isTableCaption()) - return 0; + if (!is<RenderTableCaption>(*child)) + return nullptr; } - return 0; + return nullptr; } void RenderTable::updateColumnCache() const { ASSERT(m_hasColElements); ASSERT(m_columnRenderers.isEmpty()); + ASSERT(m_effectiveColumnIndexMap.isEmpty()); ASSERT(!m_columnRenderersValid); + unsigned columnIndex = 0; for (RenderTableCol* columnRenderer = firstColumn(); columnRenderer; columnRenderer = columnRenderer->nextColumn()) { if (columnRenderer->isTableColumnGroupWithColumnChildren()) continue; m_columnRenderers.append(columnRenderer); + // FIXME: We should look to compute the effective column index successively from previous values instead of + // calling colToEffCol(), which is in O(numEffCols()). Although it's unlikely that this is a hot function. + m_effectiveColumnIndexMap.add(columnRenderer, colToEffCol(columnIndex)); + columnIndex += columnRenderer->span(); } m_columnRenderersValid = true; } +unsigned RenderTable::effectiveIndexOfColumn(const RenderTableCol& column) const +{ + if (!m_columnRenderersValid) + updateColumnCache(); + const RenderTableCol* columnToUse = &column; + if (columnToUse->isTableColumnGroupWithColumnChildren()) + columnToUse = columnToUse->nextColumn(); // First column in column-group + auto it = m_effectiveColumnIndexMap.find(columnToUse); + ASSERT(it != m_effectiveColumnIndexMap.end()); + if (it == m_effectiveColumnIndexMap.end()) + return std::numeric_limits<unsigned>::max(); + return it->value; +} + +LayoutUnit RenderTable::offsetTopForColumn(const RenderTableCol& column) const +{ + if (effectiveIndexOfColumn(column) >= numEffCols()) + return 0; + if (m_columnOffsetTop >= 0) { + ASSERT(!needsLayout()); + return m_columnOffsetTop; + } + RenderTableSection* section = topNonEmptySection(); + return m_columnOffsetTop = section ? section->offsetTop() : LayoutUnit(0); +} + +LayoutUnit RenderTable::offsetLeftForColumn(const RenderTableCol& column) const +{ + unsigned columnIndex = effectiveIndexOfColumn(column); + if (columnIndex >= numEffCols()) + return 0; + return m_columnPos[columnIndex] + LayoutUnit(m_hSpacing) + borderLeft(); +} + +LayoutUnit RenderTable::offsetWidthForColumn(const RenderTableCol& column) const +{ + const RenderTableCol* currentColumn = &column; + bool hasColumnChildren; + if ((hasColumnChildren = currentColumn->isTableColumnGroupWithColumnChildren())) + currentColumn = currentColumn->nextColumn(); // First column in column-group + unsigned numberOfEffectiveColumns = numEffCols(); + ASSERT_WITH_SECURITY_IMPLICATION(m_columnPos.size() >= numberOfEffectiveColumns + 1); + LayoutUnit width = 0; + LayoutUnit spacing = m_hSpacing; + while (currentColumn) { + unsigned columnIndex = effectiveIndexOfColumn(*currentColumn); + unsigned span = currentColumn->span(); + while (span && columnIndex < numberOfEffectiveColumns) { + width += m_columnPos[columnIndex + 1] - m_columnPos[columnIndex] - spacing; + span -= m_columns[columnIndex].span; + ++columnIndex; + if (span) + width += spacing; + } + if (!hasColumnChildren) + break; + currentColumn = currentColumn->nextColumn(); + if (!currentColumn || currentColumn->isTableColumnGroup()) + break; + width += spacing; + } + return width; +} + +LayoutUnit RenderTable::offsetHeightForColumn(const RenderTableCol& column) const +{ + if (effectiveIndexOfColumn(column) >= numEffCols()) + return 0; + if (m_columnOffsetHeight >= 0) { + ASSERT(!needsLayout()); + return m_columnOffsetHeight; + } + LayoutUnit height = 0; + for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) + height += section->offsetHeight(); + m_columnOffsetHeight = height; + return m_columnOffsetHeight; +} + RenderTableCol* RenderTable::slowColElement(unsigned col, bool* startEdge, bool* endEdge) const { ASSERT(m_hasColElements); @@ -892,42 +1028,43 @@ void RenderTable::recalcSections() const m_foot = 0; m_firstBody = 0; m_hasColElements = false; + m_hasCellColspanThatDeterminesTableWidth = hasCellColspanThatDeterminesTableWidth(); // We need to get valid pointers to caption, head, foot and first body again RenderObject* nextSibling; for (RenderObject* child = firstChild(); child; child = nextSibling) { nextSibling = child->nextSibling(); - switch (child->style()->display()) { + 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 (is<RenderTableSection>(*child)) { + RenderTableSection& section = downcast<RenderTableSection>(*child); if (!m_head) - m_head = section; + m_head = §ion; else if (!m_firstBody) - m_firstBody = section; - section->recalcCellsIfNeeded(); + m_firstBody = §ion; + section.recalcCellsIfNeeded(); } break; case TABLE_FOOTER_GROUP: - if (child->isTableSection()) { - RenderTableSection* section = toRenderTableSection(child); + if (is<RenderTableSection>(*child)) { + RenderTableSection& section = downcast<RenderTableSection>(*child); if (!m_foot) - m_foot = section; + m_foot = §ion; else if (!m_firstBody) - m_firstBody = section; - section->recalcCellsIfNeeded(); + m_firstBody = §ion; + section.recalcCellsIfNeeded(); } break; case TABLE_ROW_GROUP: - if (child->isTableSection()) { - RenderTableSection* section = toRenderTableSection(child); + if (is<RenderTableSection>(*child)) { + RenderTableSection& section = downcast<RenderTableSection>(*child); if (!m_firstBody) - m_firstBody = section; - section->recalcCellsIfNeeded(); + m_firstBody = §ion; + section.recalcCellsIfNeeded(); } break; default: @@ -937,13 +1074,10 @@ void RenderTable::recalcSections() const // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section) 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; - } + for (auto& section : childrenOfType<RenderTableSection>(*this)) { + unsigned sectionCols = section.numColumns(); + if (sectionCols > maxCols) + maxCols = sectionCols; } m_columns.resize(maxCols); @@ -954,7 +1088,7 @@ void RenderTable::recalcSections() const m_needsSectionRecalc = false; } -int RenderTable::calcBorderStart() const +LayoutUnit RenderTable::calcBorderStart() const { if (!collapseBorders()) return RenderBlock::borderStart(); @@ -963,9 +1097,9 @@ int RenderTable::calcBorderStart() const if (!numEffCols()) return 0; - unsigned borderWidth = 0; + float borderWidth = 0; - const BorderValue& tableStartBorder = style()->borderStart(); + const BorderValue& tableStartBorder = style().borderStart(); if (tableStartBorder.style() == BHIDDEN) return 0; if (tableStartBorder.style() > BHIDDEN) @@ -973,11 +1107,11 @@ int RenderTable::calcBorderStart() const if (RenderTableCol* column = colElement(0)) { // FIXME: We don't account for direction on columns and column groups. - const BorderValue& columnAdjoiningBorder = column->style()->borderStart(); + const BorderValue& columnAdjoiningBorder = column->style().borderStart(); if (columnAdjoiningBorder.style() == BHIDDEN) return 0; if (columnAdjoiningBorder.style() > BHIDDEN) - borderWidth = max(borderWidth, columnAdjoiningBorder.width()); + borderWidth = std::max(borderWidth, columnAdjoiningBorder.width()); // FIXME: This logic doesn't properly account for the first column in the first column-group case. } @@ -987,7 +1121,7 @@ int RenderTable::calcBorderStart() const return 0; if (sectionAdjoiningBorder.style() > BHIDDEN) - borderWidth = max(borderWidth, sectionAdjoiningBorder.width()); + borderWidth = std::max(borderWidth, sectionAdjoiningBorder.width()); if (const RenderTableCell* adjoiningStartCell = topNonEmptySection->firstRowCellAdjoiningTableStart()) { // FIXME: Make this work with perpendicular and flipped cells. @@ -1000,15 +1134,15 @@ int RenderTable::calcBorderStart() const return 0; if (startCellAdjoiningBorder.style() > BHIDDEN) - borderWidth = max(borderWidth, startCellAdjoiningBorder.width()); + borderWidth = std::max(borderWidth, startCellAdjoiningBorder.width()); if (firstRowAdjoiningBorder.style() > BHIDDEN) - borderWidth = max(borderWidth, firstRowAdjoiningBorder.width()); + borderWidth = std::max(borderWidth, firstRowAdjoiningBorder.width()); } } - return (borderWidth + (style()->isLeftToRightDirection() ? 0 : 1)) / 2; + return floorToInt((borderWidth + (style().isLeftToRightDirection() ? 0 : 1)) / 2); } -int RenderTable::calcBorderEnd() const +LayoutUnit RenderTable::calcBorderEnd() const { if (!collapseBorders()) return RenderBlock::borderEnd(); @@ -1017,9 +1151,9 @@ int RenderTable::calcBorderEnd() const if (!numEffCols()) return 0; - unsigned borderWidth = 0; + float borderWidth = 0; - const BorderValue& tableEndBorder = style()->borderEnd(); + const BorderValue& tableEndBorder = style().borderEnd(); if (tableEndBorder.style() == BHIDDEN) return 0; if (tableEndBorder.style() > BHIDDEN) @@ -1028,11 +1162,11 @@ int RenderTable::calcBorderEnd() const unsigned endColumn = numEffCols() - 1; if (RenderTableCol* column = colElement(endColumn)) { // FIXME: We don't account for direction on columns and column groups. - const BorderValue& columnAdjoiningBorder = column->style()->borderEnd(); + const BorderValue& columnAdjoiningBorder = column->style().borderEnd(); if (columnAdjoiningBorder.style() == BHIDDEN) return 0; if (columnAdjoiningBorder.style() > BHIDDEN) - borderWidth = max(borderWidth, columnAdjoiningBorder.width()); + borderWidth = std::max(borderWidth, columnAdjoiningBorder.width()); // FIXME: This logic doesn't properly account for the last column in the last column-group case. } @@ -1042,7 +1176,7 @@ int RenderTable::calcBorderEnd() const return 0; if (sectionAdjoiningBorder.style() > BHIDDEN) - borderWidth = max(borderWidth, sectionAdjoiningBorder.width()); + borderWidth = std::max(borderWidth, sectionAdjoiningBorder.width()); if (const RenderTableCell* adjoiningEndCell = topNonEmptySection->firstRowCellAdjoiningTableEnd()) { // FIXME: Make this work with perpendicular and flipped cells. @@ -1055,12 +1189,12 @@ int RenderTable::calcBorderEnd() const return 0; if (endCellAdjoiningBorder.style() > BHIDDEN) - borderWidth = max(borderWidth, endCellAdjoiningBorder.width()); + borderWidth = std::max(borderWidth, endCellAdjoiningBorder.width()); if (firstRowAdjoiningBorder.style() > BHIDDEN) - borderWidth = max(borderWidth, firstRowAdjoiningBorder.width()); + borderWidth = std::max(borderWidth, firstRowAdjoiningBorder.width()); } } - return (borderWidth + (style()->isLeftToRightDirection() ? 1 : 0)) / 2; + return floorToInt((borderWidth + (style().isLeftToRightDirection() ? 1 : 0)) / 2); } void RenderTable::recalcBordersInRowDirection() @@ -1070,7 +1204,7 @@ void RenderTable::recalcBordersInRowDirection() m_borderEnd = calcBorderEnd(); } -int RenderTable::borderBefore() const +LayoutUnit RenderTable::borderBefore() const { if (collapseBorders()) { recalcSectionsIfNeeded(); @@ -1079,7 +1213,7 @@ int RenderTable::borderBefore() const return RenderBlock::borderBefore(); } -int RenderTable::borderAfter() const +LayoutUnit RenderTable::borderAfter() const { if (collapseBorders()) { recalcSectionsIfNeeded(); @@ -1088,63 +1222,63 @@ int RenderTable::borderAfter() const return RenderBlock::borderAfter(); } -int RenderTable::outerBorderBefore() const +LayoutUnit RenderTable::outerBorderBefore() const { if (!collapseBorders()) return 0; - int borderWidth = 0; + LayoutUnit borderWidth = 0; if (RenderTableSection* topSection = this->topSection()) { borderWidth = topSection->outerBorderBefore(); if (borderWidth < 0) return 0; // Overridden by hidden } - const BorderValue& tb = style()->borderBefore(); + const BorderValue& tb = style().borderBefore(); if (tb.style() == BHIDDEN) return 0; if (tb.style() > BHIDDEN) - borderWidth = max<int>(borderWidth, tb.width() / 2); + borderWidth = floorToInt(std::max<LayoutUnit>(borderWidth, tb.width() / 2)); return borderWidth; } -int RenderTable::outerBorderAfter() const +LayoutUnit RenderTable::outerBorderAfter() const { if (!collapseBorders()) return 0; - int borderWidth = 0; + LayoutUnit borderWidth = 0; if (RenderTableSection* section = bottomSection()) { borderWidth = section->outerBorderAfter(); if (borderWidth < 0) return 0; // Overridden by hidden } - const BorderValue& tb = style()->borderAfter(); + const BorderValue& tb = style().borderAfter(); if (tb.style() == BHIDDEN) return 0; if (tb.style() > BHIDDEN) - borderWidth = max<int>(borderWidth, (tb.width() + 1) / 2); + borderWidth = floorToInt(std::max<LayoutUnit>(borderWidth, (tb.width() + 1) / 2)); return borderWidth; } -int RenderTable::outerBorderStart() const +LayoutUnit RenderTable::outerBorderStart() const { if (!collapseBorders()) return 0; - int borderWidth = 0; + LayoutUnit borderWidth = 0; - const BorderValue& tb = style()->borderStart(); + const BorderValue& tb = style().borderStart(); if (tb.style() == BHIDDEN) return 0; if (tb.style() > BHIDDEN) - borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 0 : 1)) / 2; + borderWidth = floorToInt((tb.width() + (style().isLeftToRightDirection() ? 0 : 1)) / 2); bool allHidden = true; for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) { - int sw = section->outerBorderStart(); + LayoutUnit sw = section->outerBorderStart(); if (sw < 0) continue; allHidden = false; - borderWidth = max(borderWidth, sw); + borderWidth = std::max(borderWidth, sw); } if (allHidden) return 0; @@ -1152,26 +1286,26 @@ int RenderTable::outerBorderStart() const return borderWidth; } -int RenderTable::outerBorderEnd() const +LayoutUnit RenderTable::outerBorderEnd() const { if (!collapseBorders()) return 0; - int borderWidth = 0; + LayoutUnit borderWidth = 0; - const BorderValue& tb = style()->borderEnd(); + const BorderValue& tb = style().borderEnd(); if (tb.style() == BHIDDEN) return 0; if (tb.style() > BHIDDEN) - borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 1 : 0)) / 2; + borderWidth = floorToInt((tb.width() + (style().isLeftToRightDirection() ? 1 : 0)) / 2); bool allHidden = true; for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) { - int sw = section->outerBorderEnd(); + LayoutUnit sw = section->outerBorderEnd(); if (sw < 0) continue; allHidden = false; - borderWidth = max(borderWidth, sw); + borderWidth = std::max(borderWidth, sw); } if (allHidden) return 0; @@ -1184,17 +1318,17 @@ RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, recalcSectionsIfNeeded(); if (section == m_head) - return 0; + return nullptr; RenderObject* prevSection = section == m_foot ? lastChild() : section->previousSibling(); while (prevSection) { - if (prevSection->isTableSection() && prevSection != m_head && prevSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || toRenderTableSection(prevSection)->numRows())) + if (is<RenderTableSection>(*prevSection) && prevSection != m_head && prevSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || downcast<RenderTableSection>(*prevSection).numRows())) break; prevSection = prevSection->previousSibling(); } if (!prevSection && m_head && (skipEmptySections == DoNotSkipEmptySections || m_head->numRows())) prevSection = m_head; - return toRenderTableSection(prevSection); + return downcast<RenderTableSection>(prevSection); } RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const @@ -1202,17 +1336,17 @@ RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, recalcSectionsIfNeeded(); if (section == m_foot) - return 0; + return nullptr; RenderObject* nextSection = section == m_head ? firstChild() : section->nextSibling(); while (nextSection) { - if (nextSection->isTableSection() && nextSection != m_head && nextSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || toRenderTableSection(nextSection)->numRows())) + if (is<RenderTableSection>(*nextSection) && nextSection != m_head && nextSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || downcast<RenderTableSection>(*nextSection).numRows())) break; nextSection = nextSection->nextSibling(); } if (!nextSection && m_foot && (skipEmptySections == DoNotSkipEmptySections || m_foot->numRows())) nextSection = m_foot; - return toRenderTableSection(nextSection); + return downcast<RenderTableSection>(nextSection); } RenderTableSection* RenderTable::bottomSection() const @@ -1223,11 +1357,11 @@ RenderTableSection* RenderTable::bottomSection() const return m_foot; for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { - if (child->isTableSection()) - return toRenderTableSection(child); + if (is<RenderTableSection>(*child)) + return downcast<RenderTableSection>(child); } - return 0; + return nullptr; } RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const @@ -1236,7 +1370,7 @@ RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const // Find the section and row to look in unsigned r = cell->rowIndex(); - RenderTableSection* section = 0; + RenderTableSection* section = nullptr; unsigned rAbove = 0; if (r > 0) { // cell is not in the first row, so use the above row in its own section @@ -1256,7 +1390,7 @@ RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const RenderTableSection::CellStruct& aboveCell = section->cellAt(rAbove, effCol); return aboveCell.primaryCell(); } else - return 0; + return nullptr; } RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const @@ -1265,7 +1399,7 @@ RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const // Find the section and row to look in unsigned r = cell->rowIndex() + cell->rowSpan() - 1; - RenderTableSection* section = 0; + RenderTableSection* section = nullptr; unsigned rBelow = 0; if (r < cell->section()->numRows() - 1) { // The cell is not in the last row, so use the next row in the section. @@ -1283,7 +1417,7 @@ RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const RenderTableSection::CellStruct& belowCell = section->cellAt(rBelow, effCol); return belowCell.primaryCell(); } else - return 0; + return nullptr; } RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const @@ -1293,7 +1427,7 @@ RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const RenderTableSection* section = cell->section(); unsigned effCol = colToEffCol(cell->col()); if (!effCol) - return 0; + return nullptr; // If we hit a colspan back up to a real cell. RenderTableSection::CellStruct& prevCell = section->cellAt(cell->rowIndex(), effCol - 1); @@ -1306,13 +1440,13 @@ RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const unsigned effCol = colToEffCol(cell->col() + cell->colSpan()); if (effCol >= numEffCols()) - return 0; + return nullptr; return cell->section()->primaryCellAt(cell->rowIndex(), effCol); } RenderBlock* RenderTable::firstLineBlock() const { - return 0; + return nullptr; } void RenderTable::updateFirstLetter() @@ -1321,40 +1455,37 @@ void RenderTable::updateFirstLetter() int RenderTable::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const { - LayoutUnit baseline = firstLineBoxBaseline(); - if (baseline != -1) - return baseline; - - return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); + return firstLineBaseline().valueOrCompute([&] { + return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); + }); } -int RenderTable::inlineBlockBaseline(LineDirectionMode) const +Optional<int> RenderTable::inlineBlockBaseline(LineDirectionMode) const { // Tables are skipped when computing an inline-block's baseline. - return -1; + return Optional<int>(); } -int RenderTable::firstLineBoxBaseline() const +Optional<int> RenderTable::firstLineBaseline() const { // The baseline of a 'table' is the same as the 'inline-table' baseline per CSS 3 Flexbox (CSS 2.1 // doesn't define the baseline of a 'table' only an 'inline-table'). // This is also needed to properly determine the baseline of a cell if it has a table child. if (isWritingModeRoot()) - return -1; + return Optional<int>(); recalcSectionsIfNeeded(); const RenderTableSection* topNonEmptySection = this->topNonEmptySection(); if (!topNonEmptySection) - return -1; + return Optional<int>(); - int baseline = topNonEmptySection->firstLineBoxBaseline(); - if (baseline > 0) - return topNonEmptySection->logicalTop() + baseline; + if (Optional<int> baseline = topNonEmptySection->firstLineBaseline()) + return Optional<int>(topNonEmptySection->logicalTop() + baseline.value()); // FIXME: A table row always has a baseline per CSS 2.1. Will this return the right value? - return -1; + return Optional<int>(); } LayoutRect RenderTable::overflowClipRect(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy, PaintPhase phase) @@ -1375,7 +1506,7 @@ LayoutRect RenderTable::overflowClipRect(const LayoutPoint& location, RenderRegi // supported. When we actually support left/right and stop mapping them to top/bottom, // we might have to hack this code first (depending on what order we do these bug fixes in). if (!m_captions.isEmpty()) { - if (style()->isHorizontalWritingMode()) { + if (style().isHorizontalWritingMode()) { rect.setHeight(height()); rect.setY(location.y()); } else { @@ -1392,10 +1523,10 @@ bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu LayoutPoint adjustedLocation = accumulatedOffset + location(); // Check kids first. - if (!hasOverflowClip() || locationInContainer.intersects(overflowClipRect(adjustedLocation, locationInContainer.region()))) { + if (!hasOverflowClip() || locationInContainer.intersects(overflowClipRect(adjustedLocation, currentRenderNamedFlowFragment()))) { 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 (is<RenderBox>(*child) && !downcast<RenderBox>(*child).hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) { + LayoutPoint childPoint = flipForWritingModeForChild(downcast<RenderBox>(child), adjustedLocation); if (child->nodeAtPoint(request, result, locationInContainer, childPoint, action)) { updateHitTestResult(result, toLayoutPoint(locationInContainer.point() - childPoint)); return true; @@ -1408,7 +1539,7 @@ bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu LayoutRect boundsRect(adjustedLocation, size()); if (visibleToHitTesting() && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && locationInContainer.intersects(boundsRect)) { updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(adjustedLocation))); - if (!result.addNodeToRectBasedTestResult(node(), request, locationInContainer, boundsRect)) + if (!result.addNodeToRectBasedTestResult(element(), request, locationInContainer, boundsRect)) return true; } @@ -1417,29 +1548,41 @@ bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu RenderTable* RenderTable::createAnonymousWithParentRenderer(const RenderObject* parent) { - RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE); - RenderTable* newTable = new (parent->renderArena()) RenderTable(0); - newTable->setDocumentForAnonymous(parent->document()); - newTable->setStyle(newStyle.release()); - return newTable; + auto table = new RenderTable(parent->document(), RenderStyle::createAnonymousStyleWithDisplay(&parent->style(), parent->style().display() == INLINE ? INLINE_TABLE : TABLE)); + table->initializeStyle(); + return table; } const BorderValue& RenderTable::tableStartBorderAdjoiningCell(const RenderTableCell* cell) const { ASSERT(cell->isFirstOrLastCellInRow()); if (hasSameDirectionAs(cell->row())) - return style()->borderStart(); + return style().borderStart(); - return style()->borderEnd(); + return style().borderEnd(); } const BorderValue& RenderTable::tableEndBorderAdjoiningCell(const RenderTableCell* cell) const { ASSERT(cell->isFirstOrLastCellInRow()); if (hasSameDirectionAs(cell->row())) - return style()->borderEnd(); + return style().borderEnd(); - return style()->borderStart(); + return style().borderStart(); +} + +void RenderTable::markForPaginationRelayoutIfNeeded() +{ + if (!view().layoutState()->isPaginated() || (!view().layoutState()->pageLogicalHeightChanged() && (!view().layoutState()->pageLogicalHeight() || view().layoutState()->pageLogicalOffset(this, logicalTop()) == pageLogicalOffset()))) + return; + + // When a table moves, we have to dirty all of the sections too. + if (!needsLayout()) + setChildNeedsLayout(MarkOnlyThis); + for (auto& child : childrenOfType<RenderTableSection>(*this)) { + if (!child.needsLayout()) + child.setChildNeedsLayout(MarkOnlyThis); + } } } |