diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/RenderTable.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/rendering/RenderTable.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderTable.cpp | 579 |
1 files changed, 372 insertions, 207 deletions
diff --git a/Source/WebCore/rendering/RenderTable.cpp b/Source/WebCore/rendering/RenderTable.cpp index 20bbe4c46..386b52780 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,8 +35,10 @@ #include "HTMLNames.h" #include "HTMLTableElement.h" #include "LayoutRepainter.h" +#include "RenderChildIterator.h" #include "RenderIterator.h" #include "RenderLayer.h" +#include "RenderNamedFlowFragment.h" #include "RenderTableCaption.h" #include "RenderTableCell.h" #include "RenderTableCol.h" @@ -49,39 +51,41 @@ namespace WebCore { using namespace HTMLNames; -RenderTable::RenderTable(Element& element, PassRef<RenderStyle> style) - : RenderBlock(element, std::move(style), 0) +RenderTable::RenderTable(Element& element, 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_hSpacing(0) - , m_vSpacing(0) + , m_hasCellColspanThatDeterminesTableWidth(false) , m_borderStart(0) , m_borderEnd(0) + , m_columnOffsetTop(-1) + , m_columnOffsetHeight(-1) { setChildrenInline(false); m_columnPos.fill(0, 1); } -RenderTable::RenderTable(Document& document, PassRef<RenderStyle> style) - : RenderBlock(document, std::move(style), 0) +RenderTable::RenderTable(Document& document, 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_hSpacing(0) - , m_vSpacing(0) + , m_hasCellColspanThatDeterminesTableWidth(false) , m_borderStart(0) , m_borderEnd(0) { @@ -109,13 +113,13 @@ void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldSty // 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)); + 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(); } @@ -134,28 +138,28 @@ 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()) { + } 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; } @@ -163,18 +167,18 @@ void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) 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) { @@ -185,34 +189,34 @@ void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) return; } - if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous() && !lastChild()->isBeforeContent()) { - toRenderTableSection(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()) { - toRenderTableSection(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) && lastBox->isTableSection()) { - RenderTableSection* section = toRenderTableSection(lastBox); - if (beforeChild == section) - beforeChild = section->firstRow(); - section->addChild(child, beforeChild); + 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); + auto section = RenderTableSection::createAnonymousWithParentRenderer(*this).release(); addChild(section, beforeChild); section->addChild(child); } @@ -225,18 +229,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*) @@ -266,10 +273,10 @@ void RenderTable::updateLogicalWidth() setMarginEnd(computedValues.m_margins.m_end); } - RenderBlock* cb = containingBlock(); + RenderBlock& cb = *containingBlock(); 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(); @@ -283,31 +290,38 @@ void RenderTable::updateLogicalWidth() // Subtract out our margins to get the available content width. LayoutUnit availableContentLogicalWidth = std::max<LayoutUnit>(0, containerWidthInInlineDirection - marginTotal); - if (shrinkToAvoidFloats() && cb->containsFloats() && !hasPerpendicularContainingBlock) { + 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(std::min<int>(availableContentLogicalWidth, maxPreferredLogicalWidth())); + setLogicalWidth(std::min(availableContentLogicalWidth, maxPreferredLogicalWidth())); + LayoutUnit maxWidth = maxPreferredLogicalWidth(); + // scaledWidthFromPercentColumns depends on m_layoutStruct in TableLayoutAlgorithmAuto, which + // maxPreferredLogicalWidth fills in. So scaledWidthFromPercentColumns has to be called after + // maxPreferredLogicalWidth. + LayoutUnit scaledWidth = m_tableLayout->scaledWidthFromPercentColumns() + bordersPaddingAndSpacingInRowDirection(); + maxWidth = std::max(scaledWidth, maxWidth); + setLogicalWidth(std::min(availableContentLogicalWidth, maxWidth)); } // Ensure we aren't smaller than our min preferred width. - setLogicalWidth(std::max<int>(logicalWidth(), minPreferredLogicalWidth())); + setLogicalWidth(std::max(logicalWidth(), minPreferredLogicalWidth())); // Ensure we aren't bigger than our max-width style. Length styleMaxLogicalWidth = style().logicalMaxWidth(); if ((styleMaxLogicalWidth.isSpecified() && !styleMaxLogicalWidth.isNegative()) || styleMaxLogicalWidth.isIntrinsic()) { LayoutUnit computedMaxLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMaxLogicalWidth, availableLogicalWidth); - setLogicalWidth(std::min<int>(logicalWidth(), computedMaxLogicalWidth)); + setLogicalWidth(std::min(logicalWidth(), computedMaxLogicalWidth)); } // Ensure we aren't smaller than our min-width style. Length styleMinLogicalWidth = style().logicalMinWidth(); if ((styleMinLogicalWidth.isSpecified() && !styleMinLogicalWidth.isNegative()) || styleMinLogicalWidth.isIntrinsic()) { LayoutUnit computedMinLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMinLogicalWidth, availableLogicalWidth); - setLogicalWidth(std::max<int>(logicalWidth(), computedMinLogicalWidth)); + setLogicalWidth(std::max(logicalWidth(), computedMinLogicalWidth)); } // Finally, with our true width determined, compute our margins for real. @@ -315,10 +329,10 @@ void RenderTable::updateLogicalWidth() setMarginEnd(0); if (!hasPerpendicularContainingBlock) { LayoutUnit containerLogicalWidthForAutoMargins = availableLogicalWidth; - if (avoidsFloats() && cb->containsFloats()) + 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); @@ -338,7 +352,7 @@ 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 = !element() || !isHTMLTableElement(element()); + bool isCSSTable = !is<HTMLTableElement>(element()); if (isCSSTable && styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive() && style().boxSizing() == CONTENT_BOX) borders = borderStart() + borderEnd() + (collapseBorders() ? LayoutUnit() : paddingStart() + paddingEnd()); @@ -347,47 +361,60 @@ LayoutUnit RenderTable::convertStyleLogicalWidthToComputedWidth(const Length& st 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 ((element() && isHTMLTableElement(element())) || 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); + return styleLogicalHeight.value() - borders; + } else if (styleLogicalHeight.isPercentOrCalculated()) + return computePercentageLogicalHeight(styleLogicalHeight).value_or(0); + else if (styleLogicalHeight.isIntrinsic()) + return computeIntrinsicLogicalContentHeightUsing(styleLogicalHeight, logicalHeight() - borderAndPadding, borderAndPadding).value_or(0); else ASSERT_NOT_REACHED(); - return std::max<LayoutUnit>(0, computedLogicalHeight); + return LayoutUnit(); } -void RenderTable::layoutCaption(RenderTableCaption* caption) +void RenderTable::layoutCaption(RenderTableCaption& caption) { - LayoutRect captionRect(caption->frameRect()); + LayoutRect captionRect(caption.frameRect()); - if (caption->needsLayout()) { + if (caption.needsLayout()) { // The margins may not be available but ensure the caption is at least located beneath any previous sibling caption // so that it does not mistakenly think any floats in the previous caption intrude into it. - caption->setLogicalLocation(LayoutPoint(caption->marginStart(), caption->marginBefore() + logicalHeight())); + caption.setLogicalLocation(LayoutPoint(caption.marginStart(), caption.marginBefore() + logicalHeight())); // If RenderTableCaption ever gets a layout() function, use it here. - caption->layoutIfNeeded(); + caption.layoutIfNeeded(); } // Apply the margins to the location now that they are definitely available from layout - caption->setLogicalLocation(LayoutPoint(caption->marginStart(), caption->marginBefore() + logicalHeight())); + caption.setLogicalLocation(LayoutPoint(caption.marginStart(), caption.marginBefore() + logicalHeight())); - if (!selfNeedsLayout() && caption->checkForRepaintDuringLayout()) - caption->repaintDuringLayoutIfMoved(captionRect); + if (!selfNeedsLayout() && caption.checkForRepaintDuringLayout()) + caption.repaintDuringLayoutIfMoved(captionRect); - setLogicalHeight(logicalHeight() + caption->logicalHeight() + caption->marginBefore() + caption->marginAfter()); + setLogicalHeight(logicalHeight() + caption.logicalHeight() + caption.marginBefore() + caption.marginAfter()); +} + +void RenderTable::layoutCaptions(BottomCaptionLayoutPhase bottomCaptionLayoutPhase) +{ + if (m_captions.isEmpty()) + return; + // FIXME: Collapse caption margin. + for (unsigned i = 0; i < m_captions.size(); ++i) { + if ((bottomCaptionLayoutPhase == BottomCaptionLayoutPhase::Yes && m_captions[i]->style().captionSide() != CAPBOTTOM) + || (bottomCaptionLayoutPhase == BottomCaptionLayoutPhase::No && m_captions[i]->style().captionSide() == CAPBOTTOM)) + continue; + layoutCaption(*m_captions[i]); + } } -void RenderTable::distributeExtraLogicalHeight(int extraLogicalHeight) +void RenderTable::distributeExtraLogicalHeight(LayoutUnit extraLogicalHeight) { if (extraLogicalHeight <= 0) return; @@ -403,10 +430,12 @@ void RenderTable::distributeExtraLogicalHeight(int extraLogicalHeight) void RenderTable::simplifiedNormalFlowLayout() { + layoutCaptions(); for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) { section->layoutIfNeeded(); section->computeOverflowFromCells(); } + layoutCaptions(BottomCaptionLayoutPhase::Yes); } void RenderTable::layout() @@ -421,13 +450,13 @@ 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); - LayoutUnit oldLogicalWidth = logicalWidth(); + LayoutUnit oldLogicalHeight = logicalHeight(); + setLogicalHeight(0); updateLogicalWidth(); if (logicalWidth() != oldLogicalWidth) { @@ -449,8 +478,8 @@ void RenderTable::layout() bool collapsing = collapseBorders(); for (auto& child : childrenOfType<RenderElement>(*this)) { - if (child.isTableSection()) { - RenderTableSection& section = toRenderTableSection(child); + if (is<RenderTableSection>(child)) { + RenderTableSection& section = downcast<RenderTableSection>(child); if (m_columnLogicalWidthChanged) section.setChildNeedsLayout(MarkOnlyThis); section.layoutIfNeeded(); @@ -458,8 +487,8 @@ void RenderTable::layout() if (collapsing) section.recalcOuterBorder(); ASSERT(!section.needsLayout()); - } else if (child.isRenderTableCol()) { - toRenderTableCol(child).layoutIfNeeded(); + } else if (is<RenderTableCol>(child)) { + downcast<RenderTableCol>(child).layoutIfNeeded(); ASSERT(!child.needsLayout()); } } @@ -470,17 +499,10 @@ void RenderTable::layout() bool sectionMoved = false; LayoutUnit movedSectionLogicalTop = 0; - // FIXME: Collapse caption margin. - 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 = std::min(logicalHeight(), oldTableLogicalTop); - } + layoutCaptions(); + if (!m_captions.isEmpty() && logicalHeight() != oldTableLogicalTop) { + sectionMoved = true; + movedSectionLogicalTop = std::min(logicalHeight(), oldTableLogicalTop); } LayoutUnit borderAndPaddingBefore = borderBefore() + (collapsing ? LayoutUnit() : paddingBefore()); @@ -494,22 +516,22 @@ void RenderTable::layout() LayoutUnit computedLogicalHeight = 0; Length logicalHeightLength = style().logicalHeight(); - if (logicalHeightLength.isSpecified() && logicalHeightLength.isPositive()) + if (logicalHeightLength.isIntrinsic() || (logicalHeightLength.isSpecified() && logicalHeightLength.isPositive())) computedLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalHeightLength); Length logicalMaxHeightLength = style().logicalMaxHeight(); - if (logicalMaxHeightLength.isSpecified() && !logicalMaxHeightLength.isNegative()) { + if (logicalMaxHeightLength.isIntrinsic() || (logicalMaxHeightLength.isSpecified() && !logicalMaxHeightLength.isNegative())) { LayoutUnit computedMaxLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMaxHeightLength); computedLogicalHeight = std::min(computedLogicalHeight, computedMaxLogicalHeight); } Length logicalMinHeightLength = style().logicalMinHeight(); - if (logicalMinHeightLength.isSpecified() && !logicalMinHeightLength.isNegative()) { + if (logicalMinHeightLength.isIntrinsic() || (logicalMinHeightLength.isSpecified() && !logicalMinHeightLength.isNegative())) { LayoutUnit computedMinLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMinHeightLength); computedLogicalHeight = std::max(computedLogicalHeight, computedMinLogicalHeight); } - distributeExtraLogicalHeight(floorToInt(computedLogicalHeight - totalSectionLogicalHeight)); + distributeExtraLogicalHeight(computedLogicalHeight - totalSectionLogicalHeight); for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) section->layoutRows(); @@ -539,24 +561,23 @@ void RenderTable::layout() setLogicalHeight(logicalHeight() + borderAndPaddingAfter); - for (unsigned i = 0; i < m_captions.size(); i++) { - if (m_captions[i]->style().captionSide() != CAPBOTTOM) - continue; - layoutCaption(m_captions[i]); - } + layoutCaptions(BottomCaptionLayoutPhase::Yes); if (isOutOfFlowPositioned()) 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(); @@ -577,14 +598,48 @@ void RenderTable::layout() 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. void RenderTable::recalcCollapsedBorders() { if (m_collapsedBordersValid) return; - m_collapsedBordersValid = true; m_collapsedBorders.clear(); - for (auto& section : childrenOfType<RenderTableSection>(*this)) { for (RenderTableRow* row = section.firstRow(); row; row = row->nextRow()) { for (RenderTableCell* cell = row->firstCell(); cell; cell = cell->nextCell()) { @@ -594,21 +649,21 @@ void RenderTable::recalcCollapsedBorders() } } 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); } @@ -629,10 +684,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; @@ -647,7 +701,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) && hasVisibleBoxDecorations() && style().visibility() == VISIBLE) paintBoxDecorations(paintInfo, paintOffset); if (paintPhase == PaintPhaseMask) { @@ -720,14 +774,14 @@ void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& p LayoutRect rect(paintOffset, size()); subtractCaptionRect(rect); - BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context); - if (!boxShadowShouldBeAppliedToBackground(bleedAvoidance)) - paintBoxShadow(paintInfo, rect, &style(), Normal); + BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context()); + if (!boxShadowShouldBeAppliedToBackground(rect.location(), bleedAvoidance)) + paintBoxShadow(paintInfo, rect, style(), Normal); paintBackground(paintInfo, rect, bleedAvoidance); - paintBoxShadow(paintInfo, rect, &style(), Inset); + paintBoxShadow(paintInfo, rect, style(), Inset); - if (style().hasBorder() && !collapseBorders()) - paintBorder(paintInfo, rect, &style()); + if (style().hasVisibleBorderDecoration() && !collapseBorders()) + paintBorder(paintInfo, rect, style()); } void RenderTable::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) @@ -760,7 +814,7 @@ void RenderTable::computePreferredLogicalWidths() computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); - int bordersPaddingAndSpacing = bordersPaddingAndSpacingInRowDirection(); + LayoutUnit bordersPaddingAndSpacing = bordersPaddingAndSpacingInRowDirection(); m_minPreferredLogicalWidth += bordersPaddingAndSpacing; m_maxPreferredLogicalWidth += bordersPaddingAndSpacing; @@ -769,14 +823,14 @@ void RenderTable::computePreferredLogicalWidths() for (unsigned i = 0; i < m_captions.size(); i++) 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. + auto& 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. + // 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())); @@ -819,6 +873,10 @@ 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 (auto& section : childrenOfType<RenderTableSection>(*this)) { @@ -833,32 +891,117 @@ void RenderTable::appendColumn(unsigned span) RenderTableCol* RenderTable::firstColumn() const { - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (child->isRenderTableCol()) - return toRenderTableCol(child); + for (auto& child : childrenOfType<RenderObject>(*this)) { + if (is<RenderTableCol>(child)) + return &const_cast<RenderTableCol&>(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] + 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); @@ -893,6 +1036,7 @@ 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; @@ -904,31 +1048,31 @@ void RenderTable::recalcSections() const 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: @@ -952,7 +1096,7 @@ void RenderTable::recalcSections() const m_needsSectionRecalc = false; } -int RenderTable::calcBorderStart() const +LayoutUnit RenderTable::calcBorderStart() const { if (!collapseBorders()) return RenderBlock::borderStart(); @@ -961,7 +1105,7 @@ int RenderTable::calcBorderStart() const if (!numEffCols()) return 0; - unsigned borderWidth = 0; + float borderWidth = 0; const BorderValue& tableStartBorder = style().borderStart(); if (tableStartBorder.style() == BHIDDEN) @@ -1003,10 +1147,10 @@ int RenderTable::calcBorderStart() const borderWidth = std::max(borderWidth, firstRowAdjoiningBorder.width()); } } - return (borderWidth + (style().isLeftToRightDirection() ? 0 : 1)) / 2; + return CollapsedBorderValue::adjustedCollapsedBorderWidth(borderWidth, document().deviceScaleFactor(), !style().isLeftToRightDirection()); } -int RenderTable::calcBorderEnd() const +LayoutUnit RenderTable::calcBorderEnd() const { if (!collapseBorders()) return RenderBlock::borderEnd(); @@ -1015,7 +1159,7 @@ int RenderTable::calcBorderEnd() const if (!numEffCols()) return 0; - unsigned borderWidth = 0; + float borderWidth = 0; const BorderValue& tableEndBorder = style().borderEnd(); if (tableEndBorder.style() == BHIDDEN) @@ -1058,7 +1202,7 @@ int RenderTable::calcBorderEnd() const borderWidth = std::max(borderWidth, firstRowAdjoiningBorder.width()); } } - return (borderWidth + (style().isLeftToRightDirection() ? 1 : 0)) / 2; + return CollapsedBorderValue::adjustedCollapsedBorderWidth(borderWidth, document().deviceScaleFactor(), style().isLeftToRightDirection()); } void RenderTable::recalcBordersInRowDirection() @@ -1068,7 +1212,7 @@ void RenderTable::recalcBordersInRowDirection() m_borderEnd = calcBorderEnd(); } -int RenderTable::borderBefore() const +LayoutUnit RenderTable::borderBefore() const { if (collapseBorders()) { recalcSectionsIfNeeded(); @@ -1077,7 +1221,7 @@ int RenderTable::borderBefore() const return RenderBlock::borderBefore(); } -int RenderTable::borderAfter() const +LayoutUnit RenderTable::borderAfter() const { if (collapseBorders()) { recalcSectionsIfNeeded(); @@ -1086,11 +1230,11 @@ 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) @@ -1099,16 +1243,18 @@ int RenderTable::outerBorderBefore() const const BorderValue& tb = style().borderBefore(); if (tb.style() == BHIDDEN) return 0; - if (tb.style() > BHIDDEN) - borderWidth = std::max<int>(borderWidth, tb.width() / 2); + if (tb.style() > BHIDDEN) { + LayoutUnit collapsedBorderWidth = std::max<LayoutUnit>(borderWidth, tb.width() / 2); + borderWidth = floorToDevicePixel(collapsedBorderWidth, document().deviceScaleFactor()); + } 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(); @@ -1118,27 +1264,30 @@ int RenderTable::outerBorderAfter() const const BorderValue& tb = style().borderAfter(); if (tb.style() == BHIDDEN) return 0; - if (tb.style() > BHIDDEN) - borderWidth = std::max<int>(borderWidth, (tb.width() + 1) / 2); + if (tb.style() > BHIDDEN) { + float deviceScaleFactor = document().deviceScaleFactor(); + LayoutUnit collapsedBorderWidth = std::max<LayoutUnit>(borderWidth, (tb.width() + (1 / deviceScaleFactor)) / 2); + borderWidth = floorToDevicePixel(collapsedBorderWidth, deviceScaleFactor); + } 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(); if (tb.style() == BHIDDEN) return 0; if (tb.style() > BHIDDEN) - borderWidth = (tb.width() + (style().isLeftToRightDirection() ? 0 : 1)) / 2; + return CollapsedBorderValue::adjustedCollapsedBorderWidth(tb.width(), document().deviceScaleFactor(), !style().isLeftToRightDirection()); 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; @@ -1150,22 +1299,22 @@ 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(); if (tb.style() == BHIDDEN) return 0; if (tb.style() > BHIDDEN) - borderWidth = (tb.width() + (style().isLeftToRightDirection() ? 1 : 0)) / 2; + return CollapsedBorderValue::adjustedCollapsedBorderWidth(tb.width(), document().deviceScaleFactor(), style().isLeftToRightDirection()); 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; @@ -1182,17 +1331,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 @@ -1200,17 +1349,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 @@ -1221,11 +1370,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 @@ -1234,7 +1383,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 @@ -1254,7 +1403,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 @@ -1263,7 +1412,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. @@ -1281,7 +1430,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 @@ -1291,7 +1440,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); @@ -1304,55 +1453,52 @@ 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() +void RenderTable::updateFirstLetter(RenderTreeMutationIsAllowed) { } int RenderTable::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const { - LayoutUnit baseline = firstLineBaseline(); - if (baseline != -1) - return baseline; - - return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); + return valueOrCompute(firstLineBaseline(), [&] { + return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); + }); } -int RenderTable::inlineBlockBaseline(LineDirectionMode) const +std::optional<int> RenderTable::inlineBlockBaseline(LineDirectionMode) const { // Tables are skipped when computing an inline-block's baseline. - return -1; + return std::optional<int>(); } -int RenderTable::firstLineBaseline() const +std::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 std::optional<int>(); recalcSectionsIfNeeded(); const RenderTableSection* topNonEmptySection = this->topNonEmptySection(); if (!topNonEmptySection) - return -1; + return std::optional<int>(); - int baseline = topNonEmptySection->firstLineBaseline(); - if (baseline > 0) - return topNonEmptySection->logicalTop() + baseline; + if (std::optional<int> baseline = topNonEmptySection->firstLineBaseline()) + return std::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 std::optional<int>(); } LayoutRect RenderTable::overflowClipRect(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy, PaintPhase phase) @@ -1390,10 +1536,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; @@ -1413,29 +1559,48 @@ bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu return false; } -RenderTable* RenderTable::createAnonymousWithParentRenderer(const RenderObject* parent) +std::unique_ptr<RenderTable> RenderTable::createTableWithStyle(Document& document, const RenderStyle& style) { - auto table = new RenderTable(parent->document(), RenderStyle::createAnonymousStyleWithDisplay(&parent->style(), TABLE)); + auto table = std::make_unique<RenderTable>(document, RenderStyle::createAnonymousStyleWithDisplay(style, style.display() == INLINE ? INLINE_TABLE : TABLE)); table->initializeStyle(); return table; } -const BorderValue& RenderTable::tableStartBorderAdjoiningCell(const RenderTableCell* cell) const +std::unique_ptr<RenderTable> RenderTable::createAnonymousWithParentRenderer(const RenderElement& parent) +{ + return RenderTable::createTableWithStyle(parent.document(), parent.style()); +} + +const BorderValue& RenderTable::tableStartBorderAdjoiningCell(const RenderTableCell& cell) const { - ASSERT(cell->isFirstOrLastCellInRow()); - if (hasSameDirectionAs(cell->row())) + ASSERT(cell.isFirstOrLastCellInRow()); + if (isDirectionSame(this, cell.row())) return style().borderStart(); return style().borderEnd(); } -const BorderValue& RenderTable::tableEndBorderAdjoiningCell(const RenderTableCell* cell) const +const BorderValue& RenderTable::tableEndBorderAdjoiningCell(const RenderTableCell& cell) const { - ASSERT(cell->isFirstOrLastCellInRow()); - if (hasSameDirectionAs(cell->row())) + ASSERT(cell.isFirstOrLastCellInRow()); + if (isDirectionSame(this, cell.row())) return style().borderEnd(); 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); + } +} + } |