2 * Copyright (C) 1997 Martin Jones (mjones@kde.org)
3 * (C) 1997 Torben Weis (weis@kde.org)
4 * (C) 1998 Waldo Bastian (bastian@kde.org)
5 * (C) 1999 Lars Knoll (knoll@kde.org)
6 * (C) 1999 Antti Koivisto (koivisto@kde.org)
7 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2014 Apple Inc. All rights reserved.
8 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
27 #include "RenderTable.h"
29 #include "AutoTableLayout.h"
30 #include "CollapsedBorderValue.h"
32 #include "FixedTableLayout.h"
33 #include "FrameView.h"
34 #include "HitTestResult.h"
35 #include "HTMLNames.h"
36 #include "HTMLTableElement.h"
37 #include "LayoutRepainter.h"
38 #include "RenderChildIterator.h"
39 #include "RenderIterator.h"
40 #include "RenderLayer.h"
41 #include "RenderNamedFlowFragment.h"
42 #include "RenderTableCaption.h"
43 #include "RenderTableCell.h"
44 #include "RenderTableCol.h"
45 #include "RenderTableSection.h"
46 #include "RenderView.h"
47 #include "StyleInheritedData.h"
48 #include <wtf/StackStats.h>
52 using namespace HTMLNames;
54 RenderTable::RenderTable(Element& element, RenderStyle&& style)
55 : RenderBlock(element, WTFMove(style), 0)
58 , m_firstBody(nullptr)
59 , m_currentBorder(nullptr)
60 , m_collapsedBordersValid(false)
61 , m_collapsedEmptyBorderIsPresent(false)
62 , m_hasColElements(false)
63 , m_needsSectionRecalc(false)
64 , m_columnLogicalWidthChanged(false)
65 , m_columnRenderersValid(false)
66 , m_hasCellColspanThatDeterminesTableWidth(false)
69 , m_columnOffsetTop(-1)
70 , m_columnOffsetHeight(-1)
72 setChildrenInline(false);
73 m_columnPos.fill(0, 1);
76 RenderTable::RenderTable(Document& document, RenderStyle&& style)
77 : RenderBlock(document, WTFMove(style), 0)
80 , m_firstBody(nullptr)
81 , m_currentBorder(nullptr)
82 , m_collapsedBordersValid(false)
83 , m_collapsedEmptyBorderIsPresent(false)
84 , m_hasColElements(false)
85 , m_needsSectionRecalc(false)
86 , m_columnLogicalWidthChanged(false)
87 , m_columnRenderersValid(false)
88 , m_hasCellColspanThatDeterminesTableWidth(false)
92 setChildrenInline(false);
93 m_columnPos.fill(0, 1);
96 RenderTable::~RenderTable()
100 void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
102 RenderBlock::styleDidChange(diff, oldStyle);
103 propagateStyleToAnonymousChildren(PropagateToAllChildren);
105 ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO;
107 // In the collapsed border model, there is no cell spacing.
108 m_hSpacing = collapseBorders() ? 0 : style().horizontalBorderSpacing();
109 m_vSpacing = collapseBorders() ? 0 : style().verticalBorderSpacing();
110 m_columnPos[0] = m_hSpacing;
112 if (!m_tableLayout || style().tableLayout() != oldTableLayout) {
113 // According to the CSS2 spec, you only use fixed table layout if an
114 // explicit width is specified on the table. Auto width implies auto table layout.
115 if (style().tableLayout() == TFIXED && !style().logicalWidth().isAuto())
116 m_tableLayout = std::make_unique<FixedTableLayout>(this);
118 m_tableLayout = std::make_unique<AutoTableLayout>(this);
121 // If border was changed, invalidate collapsed borders cache.
122 if (oldStyle && oldStyle->border() != style().border())
123 invalidateCollapsedBorders();
126 static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before)
130 RenderObject* o = before->previousSibling();
131 while (o && o != ptr)
132 o = o->previousSibling();
137 void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild)
139 bool wrapInAnonymousSection = !child->isOutOfFlowPositioned();
141 if (is<RenderTableCaption>(*child))
142 wrapInAnonymousSection = false;
143 else if (is<RenderTableCol>(*child)) {
144 m_hasColElements = true;
145 wrapInAnonymousSection = false;
146 } else if (is<RenderTableSection>(*child)) {
147 switch (child->style().display()) {
148 case TABLE_HEADER_GROUP:
149 resetSectionPointerIfNotBefore(m_head, beforeChild);
151 m_head = downcast<RenderTableSection>(child);
153 resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
155 m_firstBody = downcast<RenderTableSection>(child);
157 wrapInAnonymousSection = false;
159 case TABLE_FOOTER_GROUP:
160 resetSectionPointerIfNotBefore(m_foot, beforeChild);
162 m_foot = downcast<RenderTableSection>(child);
163 wrapInAnonymousSection = false;
167 case TABLE_ROW_GROUP:
168 resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
170 m_firstBody = downcast<RenderTableSection>(child);
171 wrapInAnonymousSection = false;
174 ASSERT_NOT_REACHED();
176 } else if (is<RenderTableCell>(*child) || is<RenderTableRow>(*child))
177 wrapInAnonymousSection = true;
179 wrapInAnonymousSection = true;
181 if (is<RenderTableSection>(*child))
182 setNeedsSectionRecalc();
184 if (!wrapInAnonymousSection) {
185 if (beforeChild && beforeChild->parent() != this)
186 beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
188 RenderBox::addChild(child, beforeChild);
192 if (!beforeChild && is<RenderTableSection>(lastChild()) && lastChild()->isAnonymous() && !lastChild()->isBeforeContent()) {
193 downcast<RenderTableSection>(*lastChild()).addChild(child);
197 if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) {
198 RenderObject* section = beforeChild->previousSibling();
199 if (is<RenderTableSection>(section) && section->isAnonymous()) {
200 downcast<RenderTableSection>(*section).addChild(child);
205 RenderObject* lastBox = beforeChild;
206 while (lastBox && lastBox->parent()->isAnonymous() && !is<RenderTableSection>(*lastBox) && lastBox->style().display() != TABLE_CAPTION && lastBox->style().display() != TABLE_COLUMN_GROUP)
207 lastBox = lastBox->parent();
208 if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox) && lastBox->isTableSection()) {
209 RenderTableSection& section = downcast<RenderTableSection>(*lastBox);
210 if (beforeChild == §ion)
211 beforeChild = section.firstRow();
212 section.addChild(child, beforeChild);
216 if (beforeChild && !is<RenderTableSection>(*beforeChild) && beforeChild->style().display() != TABLE_CAPTION && beforeChild->style().display() != TABLE_COLUMN_GROUP)
217 beforeChild = nullptr;
219 RenderTableSection* section = RenderTableSection::createAnonymousWithParentRenderer(this);
220 addChild(section, beforeChild);
221 section->addChild(child);
224 void RenderTable::addCaption(const RenderTableCaption* caption)
226 ASSERT(m_captions.find(caption) == notFound);
227 m_captions.append(const_cast<RenderTableCaption*>(caption));
230 void RenderTable::removeCaption(const RenderTableCaption* oldCaption)
232 bool removed = m_captions.removeFirst(oldCaption);
233 ASSERT_UNUSED(removed, removed);
236 void RenderTable::invalidateCachedColumns()
238 m_columnRenderersValid = false;
239 m_columnRenderers.resize(0);
240 m_effectiveColumnIndexMap.clear();
243 void RenderTable::invalidateCachedColumnOffsets()
245 m_columnOffsetTop = -1;
246 m_columnOffsetHeight = -1;
249 void RenderTable::addColumn(const RenderTableCol*)
251 invalidateCachedColumns();
254 void RenderTable::removeColumn(const RenderTableCol*)
256 invalidateCachedColumns();
257 // We don't really need to recompute our sections, but we need to update our
258 // column count and whether we have a column. Currently, we only have one
259 // size-fit-all flag but we may have to consider splitting it.
260 setNeedsSectionRecalc();
263 void RenderTable::updateLogicalWidth()
265 recalcSectionsIfNeeded();
267 if (isOutOfFlowPositioned()) {
268 LogicalExtentComputedValues computedValues;
269 computePositionedLogicalWidth(computedValues);
270 setLogicalWidth(computedValues.m_extent);
271 setLogicalLeft(computedValues.m_position);
272 setMarginStart(computedValues.m_margins.m_start);
273 setMarginEnd(computedValues.m_margins.m_end);
276 RenderBlock& cb = *containingBlock();
278 LayoutUnit availableLogicalWidth = containingBlockLogicalWidthForContent();
279 bool hasPerpendicularContainingBlock = cb.style().isHorizontalWritingMode() != style().isHorizontalWritingMode();
280 LayoutUnit containerWidthInInlineDirection = hasPerpendicularContainingBlock ? perpendicularContainingBlockLogicalHeight() : availableLogicalWidth;
282 Length styleLogicalWidth = style().logicalWidth();
283 if ((styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive()) || styleLogicalWidth.isIntrinsic())
284 setLogicalWidth(convertStyleLogicalWidthToComputedWidth(styleLogicalWidth, containerWidthInInlineDirection));
286 // Subtract out any fixed margins from our available width for auto width tables.
287 LayoutUnit marginStart = minimumValueForLength(style().marginStart(), availableLogicalWidth);
288 LayoutUnit marginEnd = minimumValueForLength(style().marginEnd(), availableLogicalWidth);
289 LayoutUnit marginTotal = marginStart + marginEnd;
291 // Subtract out our margins to get the available content width.
292 LayoutUnit availableContentLogicalWidth = std::max<LayoutUnit>(0, containerWidthInInlineDirection - marginTotal);
293 if (shrinkToAvoidFloats() && cb.containsFloats() && !hasPerpendicularContainingBlock) {
294 // FIXME: Work with regions someday.
295 availableContentLogicalWidth = shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, cb, 0);
298 // Ensure we aren't bigger than our available width.
299 setLogicalWidth(std::min(availableContentLogicalWidth, maxPreferredLogicalWidth()));
300 LayoutUnit maxWidth = maxPreferredLogicalWidth();
301 // scaledWidthFromPercentColumns depends on m_layoutStruct in TableLayoutAlgorithmAuto, which
302 // maxPreferredLogicalWidth fills in. So scaledWidthFromPercentColumns has to be called after
303 // maxPreferredLogicalWidth.
304 LayoutUnit scaledWidth = m_tableLayout->scaledWidthFromPercentColumns() + bordersPaddingAndSpacingInRowDirection();
305 maxWidth = std::max(scaledWidth, maxWidth);
306 setLogicalWidth(std::min(availableContentLogicalWidth, maxWidth));
309 // Ensure we aren't smaller than our min preferred width.
310 setLogicalWidth(std::max(logicalWidth(), minPreferredLogicalWidth()));
313 // Ensure we aren't bigger than our max-width style.
314 Length styleMaxLogicalWidth = style().logicalMaxWidth();
315 if ((styleMaxLogicalWidth.isSpecified() && !styleMaxLogicalWidth.isNegative()) || styleMaxLogicalWidth.isIntrinsic()) {
316 LayoutUnit computedMaxLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMaxLogicalWidth, availableLogicalWidth);
317 setLogicalWidth(std::min(logicalWidth(), computedMaxLogicalWidth));
320 // Ensure we aren't smaller than our min-width style.
321 Length styleMinLogicalWidth = style().logicalMinWidth();
322 if ((styleMinLogicalWidth.isSpecified() && !styleMinLogicalWidth.isNegative()) || styleMinLogicalWidth.isIntrinsic()) {
323 LayoutUnit computedMinLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMinLogicalWidth, availableLogicalWidth);
324 setLogicalWidth(std::max(logicalWidth(), computedMinLogicalWidth));
327 // Finally, with our true width determined, compute our margins for real.
330 if (!hasPerpendicularContainingBlock) {
331 LayoutUnit containerLogicalWidthForAutoMargins = availableLogicalWidth;
332 if (avoidsFloats() && cb.containsFloats())
333 containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidthInRegion(0); // FIXME: Work with regions someday.
334 ComputedMarginValues marginValues;
335 bool hasInvertedDirection = cb.style().isLeftToRightDirection() == style().isLeftToRightDirection();
336 computeInlineDirectionMargins(cb, containerLogicalWidthForAutoMargins, logicalWidth(),
337 hasInvertedDirection ? marginValues.m_start : marginValues.m_end,
338 hasInvertedDirection ? marginValues.m_end : marginValues.m_start);
339 setMarginStart(marginValues.m_start);
340 setMarginEnd(marginValues.m_end);
342 setMarginStart(minimumValueForLength(style().marginStart(), availableLogicalWidth));
343 setMarginEnd(minimumValueForLength(style().marginEnd(), availableLogicalWidth));
347 // This method takes a RenderStyle's logical width, min-width, or max-width length and computes its actual value.
348 LayoutUnit RenderTable::convertStyleLogicalWidthToComputedWidth(const Length& styleLogicalWidth, LayoutUnit availableWidth)
350 if (styleLogicalWidth.isIntrinsic())
351 return computeIntrinsicLogicalWidthUsing(styleLogicalWidth, availableWidth, bordersPaddingAndSpacingInRowDirection());
353 // HTML tables' width styles already include borders and paddings, but CSS tables' width styles do not.
354 LayoutUnit borders = 0;
355 bool isCSSTable = !is<HTMLTableElement>(element());
356 if (isCSSTable && styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive() && style().boxSizing() == CONTENT_BOX)
357 borders = borderStart() + borderEnd() + (collapseBorders() ? LayoutUnit() : paddingStart() + paddingEnd());
359 return minimumValueForLength(styleLogicalWidth, availableWidth) + borders;
362 LayoutUnit RenderTable::convertStyleLogicalHeightToComputedHeight(const Length& styleLogicalHeight)
364 LayoutUnit borderAndPaddingBefore = borderBefore() + (collapseBorders() ? LayoutUnit() : paddingBefore());
365 LayoutUnit borderAndPaddingAfter = borderAfter() + (collapseBorders() ? LayoutUnit() : paddingAfter());
366 LayoutUnit borderAndPadding = borderAndPaddingBefore + borderAndPaddingAfter;
367 if (styleLogicalHeight.isFixed()) {
368 // HTML tables size as though CSS height includes border/padding, CSS tables do not.
369 LayoutUnit borders = LayoutUnit();
370 // FIXME: We cannot apply box-sizing: content-box on <table> which other browsers allow.
371 if (is<HTMLTableElement>(element()) || style().boxSizing() == BORDER_BOX) {
372 borders = borderAndPadding;
374 return styleLogicalHeight.value() - borders;
375 } else if (styleLogicalHeight.isPercentOrCalculated())
376 return computePercentageLogicalHeight(styleLogicalHeight).valueOr(0);
377 else if (styleLogicalHeight.isIntrinsic())
378 return computeIntrinsicLogicalContentHeightUsing(styleLogicalHeight, logicalHeight() - borderAndPadding, borderAndPadding).valueOr(0);
380 ASSERT_NOT_REACHED();
384 void RenderTable::layoutCaption(RenderTableCaption* caption)
386 LayoutRect captionRect(caption->frameRect());
388 if (caption->needsLayout()) {
389 // The margins may not be available but ensure the caption is at least located beneath any previous sibling caption
390 // so that it does not mistakenly think any floats in the previous caption intrude into it.
391 caption->setLogicalLocation(LayoutPoint(caption->marginStart(), caption->marginBefore() + logicalHeight()));
392 // If RenderTableCaption ever gets a layout() function, use it here.
393 caption->layoutIfNeeded();
395 // Apply the margins to the location now that they are definitely available from layout
396 caption->setLogicalLocation(LayoutPoint(caption->marginStart(), caption->marginBefore() + logicalHeight()));
398 if (!selfNeedsLayout() && caption->checkForRepaintDuringLayout())
399 caption->repaintDuringLayoutIfMoved(captionRect);
401 setLogicalHeight(logicalHeight() + caption->logicalHeight() + caption->marginBefore() + caption->marginAfter());
404 void RenderTable::distributeExtraLogicalHeight(LayoutUnit extraLogicalHeight)
406 if (extraLogicalHeight <= 0)
409 // FIXME: Distribute the extra logical height between all table sections instead of giving it all to the first one.
410 if (RenderTableSection* section = firstBody())
411 extraLogicalHeight -= section->distributeExtraLogicalHeightToRows(extraLogicalHeight);
413 // FIXME: We really would like to enable this ASSERT to ensure that all the extra space has been distributed.
414 // However our current distribution algorithm does not round properly and thus we can have some remaining height.
415 // ASSERT(!topSection() || !extraLogicalHeight);
418 void RenderTable::simplifiedNormalFlowLayout()
420 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) {
421 section->layoutIfNeeded();
422 section->computeOverflowFromCells();
426 void RenderTable::layout()
428 StackStats::LayoutCheckPoint layoutCheckPoint;
429 ASSERT(needsLayout());
431 if (simplifiedLayout())
434 recalcSectionsIfNeeded();
435 // FIXME: We should do this recalc lazily in borderStart/borderEnd so that we don't have to make sure
436 // to call this before we call borderStart/borderEnd to avoid getting a stale value.
437 recalcBordersInRowDirection();
439 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
440 LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
442 LayoutUnit oldLogicalWidth = logicalWidth();
443 LayoutUnit oldLogicalHeight = logicalHeight();
445 updateLogicalWidth();
447 if (logicalWidth() != oldLogicalWidth) {
448 for (unsigned i = 0; i < m_captions.size(); i++)
449 m_captions[i]->setNeedsLayout(MarkOnlyThis);
451 // FIXME: The optimisation below doesn't work since the internal table
452 // layout could have changed. we need to add a flag to the table
453 // layout that tells us if something has changed in the min max
454 // calculations to do it correctly.
455 // if ( oldWidth != width() || columns.size() + 1 != columnPos.size() )
456 m_tableLayout->layout();
458 LayoutUnit totalSectionLogicalHeight = 0;
459 LayoutUnit oldTableLogicalTop = 0;
460 for (unsigned i = 0; i < m_captions.size(); i++)
461 oldTableLogicalTop += m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter();
463 bool collapsing = collapseBorders();
465 for (auto& child : childrenOfType<RenderElement>(*this)) {
466 if (is<RenderTableSection>(child)) {
467 RenderTableSection& section = downcast<RenderTableSection>(child);
468 if (m_columnLogicalWidthChanged)
469 section.setChildNeedsLayout(MarkOnlyThis);
470 section.layoutIfNeeded();
471 totalSectionLogicalHeight += section.calcRowLogicalHeight();
473 section.recalcOuterBorder();
474 ASSERT(!section.needsLayout());
475 } else if (is<RenderTableCol>(child)) {
476 downcast<RenderTableCol>(child).layoutIfNeeded();
477 ASSERT(!child.needsLayout());
481 // If any table section moved vertically, we will just repaint everything from that
482 // section down (it is quite unlikely that any of the following sections
484 bool sectionMoved = false;
485 LayoutUnit movedSectionLogicalTop = 0;
487 // FIXME: Collapse caption margin.
488 if (!m_captions.isEmpty()) {
489 for (unsigned i = 0; i < m_captions.size(); i++) {
490 if (m_captions[i]->style().captionSide() == CAPBOTTOM)
492 layoutCaption(m_captions[i]);
494 if (logicalHeight() != oldTableLogicalTop) {
496 movedSectionLogicalTop = std::min(logicalHeight(), oldTableLogicalTop);
500 LayoutUnit borderAndPaddingBefore = borderBefore() + (collapsing ? LayoutUnit() : paddingBefore());
501 LayoutUnit borderAndPaddingAfter = borderAfter() + (collapsing ? LayoutUnit() : paddingAfter());
503 setLogicalHeight(logicalHeight() + borderAndPaddingBefore);
505 if (!isOutOfFlowPositioned())
506 updateLogicalHeight();
508 LayoutUnit computedLogicalHeight = 0;
510 Length logicalHeightLength = style().logicalHeight();
511 if (logicalHeightLength.isIntrinsic() || (logicalHeightLength.isSpecified() && logicalHeightLength.isPositive()))
512 computedLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalHeightLength);
514 Length logicalMaxHeightLength = style().logicalMaxHeight();
515 if (logicalMaxHeightLength.isIntrinsic() || (logicalMaxHeightLength.isSpecified() && !logicalMaxHeightLength.isNegative())) {
516 LayoutUnit computedMaxLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMaxHeightLength);
517 computedLogicalHeight = std::min(computedLogicalHeight, computedMaxLogicalHeight);
520 Length logicalMinHeightLength = style().logicalMinHeight();
521 if (logicalMinHeightLength.isIntrinsic() || (logicalMinHeightLength.isSpecified() && !logicalMinHeightLength.isNegative())) {
522 LayoutUnit computedMinLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMinHeightLength);
523 computedLogicalHeight = std::max(computedLogicalHeight, computedMinLogicalHeight);
526 distributeExtraLogicalHeight(computedLogicalHeight - totalSectionLogicalHeight);
528 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section))
529 section->layoutRows();
531 if (!topSection() && computedLogicalHeight > totalSectionLogicalHeight && !document().inQuirksMode()) {
532 // Completely empty tables (with no sections or anything) should at least honor specified height
534 setLogicalHeight(logicalHeight() + computedLogicalHeight);
537 LayoutUnit sectionLogicalLeft = style().isLeftToRightDirection() ? borderStart() : borderEnd();
539 sectionLogicalLeft += style().isLeftToRightDirection() ? paddingStart() : paddingEnd();
541 // position the table sections
542 RenderTableSection* section = topSection();
544 if (!sectionMoved && section->logicalTop() != logicalHeight()) {
546 movedSectionLogicalTop = std::min(logicalHeight(), section->logicalTop()) + (style().isHorizontalWritingMode() ? section->visualOverflowRect().y() : section->visualOverflowRect().x());
548 section->setLogicalLocation(LayoutPoint(sectionLogicalLeft, logicalHeight()));
550 setLogicalHeight(logicalHeight() + section->logicalHeight());
551 section = sectionBelow(section);
554 setLogicalHeight(logicalHeight() + borderAndPaddingAfter);
556 for (unsigned i = 0; i < m_captions.size(); i++) {
557 if (m_captions[i]->style().captionSide() != CAPBOTTOM)
559 layoutCaption(m_captions[i]);
562 if (isOutOfFlowPositioned())
563 updateLogicalHeight();
565 // table can be containing block of positioned elements.
566 bool dimensionChanged = oldLogicalWidth != logicalWidth() || oldLogicalHeight != logicalHeight();
567 layoutPositionedObjects(dimensionChanged);
569 updateLayerTransform();
571 // Layout was changed, so probably borders too.
572 invalidateCollapsedBorders();
574 // The location or height of one or more sections may have changed.
575 invalidateCachedColumnOffsets();
577 computeOverflow(clientLogicalBottom());
581 if (view().layoutState()->pageLogicalHeight())
582 setPageLogicalOffset(view().layoutState()->pageLogicalOffset(this, logicalTop()));
584 bool didFullRepaint = repainter.repaintAfterLayout();
585 // Repaint with our new bounds if they are different from our old bounds.
586 if (!didFullRepaint && sectionMoved) {
587 if (style().isHorizontalWritingMode())
588 repaintRectangle(LayoutRect(visualOverflowRect().x(), movedSectionLogicalTop, visualOverflowRect().width(), visualOverflowRect().maxY() - movedSectionLogicalTop));
590 repaintRectangle(LayoutRect(movedSectionLogicalTop, visualOverflowRect().y(), visualOverflowRect().maxX() - movedSectionLogicalTop, visualOverflowRect().height()));
593 m_columnLogicalWidthChanged = false;
597 void RenderTable::invalidateCollapsedBorders(RenderTableCell* cellWithStyleChange)
599 m_collapsedBordersValid = false;
600 m_collapsedBorders.clear();
602 for (auto& section : childrenOfType<RenderTableSection>(*this))
603 section.clearCachedCollapsedBorders();
605 if (!m_collapsedEmptyBorderIsPresent)
608 if (cellWithStyleChange) {
609 // It is enough to invalidate just the surrounding cells when cell border style changes.
610 cellWithStyleChange->invalidateHasEmptyCollapsedBorders();
611 if (auto* below = cellBelow(cellWithStyleChange))
612 below->invalidateHasEmptyCollapsedBorders();
613 if (auto* above = cellAbove(cellWithStyleChange))
614 above->invalidateHasEmptyCollapsedBorders();
615 if (auto* before = cellBefore(cellWithStyleChange))
616 before->invalidateHasEmptyCollapsedBorders();
617 if (auto* after = cellAfter(cellWithStyleChange))
618 after->invalidateHasEmptyCollapsedBorders();
622 for (auto& section : childrenOfType<RenderTableSection>(*this)) {
623 for (auto* row = section.firstRow(); row; row = row->nextRow()) {
624 for (auto* cell = row->firstCell(); cell; cell = cell->nextCell()) {
625 ASSERT(cell->table() == this);
626 cell->invalidateHasEmptyCollapsedBorders();
630 m_collapsedEmptyBorderIsPresent = false;
633 // Collect all the unique border values that we want to paint in a sorted list.
634 void RenderTable::recalcCollapsedBorders()
636 if (m_collapsedBordersValid)
638 m_collapsedBorders.clear();
639 for (auto& section : childrenOfType<RenderTableSection>(*this)) {
640 for (RenderTableRow* row = section.firstRow(); row; row = row->nextRow()) {
641 for (RenderTableCell* cell = row->firstCell(); cell; cell = cell->nextCell()) {
642 ASSERT(cell->table() == this);
643 cell->collectBorderValues(m_collapsedBorders);
647 RenderTableCell::sortBorderValues(m_collapsedBorders);
648 m_collapsedBordersValid = true;
651 void RenderTable::addOverflowFromChildren()
653 // Add overflow from borders.
654 // Technically it's odd that we are incorporating the borders into layout overflow, which is only supposed to be about overflow from our
655 // descendant objects, but since tables don't support overflow:auto, this works out fine.
656 if (collapseBorders()) {
657 LayoutUnit rightBorderOverflow = width() + outerBorderRight() - borderRight();
658 LayoutUnit leftBorderOverflow = borderLeft() - outerBorderLeft();
659 LayoutUnit bottomBorderOverflow = height() + outerBorderBottom() - borderBottom();
660 LayoutUnit topBorderOverflow = borderTop() - outerBorderTop();
661 LayoutRect borderOverflowRect(leftBorderOverflow, topBorderOverflow, rightBorderOverflow - leftBorderOverflow, bottomBorderOverflow - topBorderOverflow);
662 if (borderOverflowRect != borderBoxRect()) {
663 addLayoutOverflow(borderOverflowRect);
664 addVisualOverflow(borderOverflowRect);
668 // Add overflow from our caption.
669 for (unsigned i = 0; i < m_captions.size(); i++)
670 addOverflowFromChild(m_captions[i]);
672 // Add overflow from our sections.
673 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section))
674 addOverflowFromChild(section);
677 void RenderTable::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
679 LayoutPoint adjustedPaintOffset = paintOffset + location();
681 PaintPhase paintPhase = paintInfo.phase;
683 if (!isDocumentElementRenderer()) {
684 LayoutRect overflowBox = visualOverflowRect();
685 flipForWritingMode(overflowBox);
686 overflowBox.moveBy(adjustedPaintOffset);
687 if (!overflowBox.intersects(paintInfo.rect))
691 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset);
692 paintObject(paintInfo, adjustedPaintOffset);
694 popContentsClip(paintInfo, paintPhase, adjustedPaintOffset);
697 void RenderTable::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
699 PaintPhase paintPhase = paintInfo.phase;
700 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasVisibleBoxDecorations() && style().visibility() == VISIBLE)
701 paintBoxDecorations(paintInfo, paintOffset);
703 if (paintPhase == PaintPhaseMask) {
704 paintMask(paintInfo, paintOffset);
708 // We're done. We don't bother painting any children.
709 if (paintPhase == PaintPhaseBlockBackground)
712 // We don't paint our own background, but we do let the kids paint their backgrounds.
713 if (paintPhase == PaintPhaseChildBlockBackgrounds)
714 paintPhase = PaintPhaseChildBlockBackground;
716 PaintInfo info(paintInfo);
717 info.phase = paintPhase;
718 info.updateSubtreePaintRootForChildren(this);
720 for (auto& box : childrenOfType<RenderBox>(*this)) {
721 if (!box.hasSelfPaintingLayer() && (box.isTableSection() || box.isTableCaption())) {
722 LayoutPoint childPoint = flipForWritingModeForChild(&box, paintOffset);
723 box.paint(info, childPoint);
727 if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style().visibility() == VISIBLE) {
728 recalcCollapsedBorders();
729 // Using our cached sorted styles, we then do individual passes,
730 // painting each style of border from lowest precedence to highest precedence.
731 info.phase = PaintPhaseCollapsedTableBorders;
732 size_t count = m_collapsedBorders.size();
733 for (size_t i = 0; i < count; ++i) {
734 m_currentBorder = &m_collapsedBorders[i];
735 for (RenderTableSection* section = bottomSection(); section; section = sectionAbove(section)) {
736 LayoutPoint childPoint = flipForWritingModeForChild(section, paintOffset);
737 section->paint(info, childPoint);
744 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style().visibility() == VISIBLE)
745 paintOutline(paintInfo, LayoutRect(paintOffset, size()));
748 void RenderTable::subtractCaptionRect(LayoutRect& rect) const
750 for (unsigned i = 0; i < m_captions.size(); i++) {
751 LayoutUnit captionLogicalHeight = m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter();
752 bool captionIsBefore = (m_captions[i]->style().captionSide() != CAPBOTTOM) ^ style().isFlippedBlocksWritingMode();
753 if (style().isHorizontalWritingMode()) {
754 rect.setHeight(rect.height() - captionLogicalHeight);
756 rect.move(0, captionLogicalHeight);
758 rect.setWidth(rect.width() - captionLogicalHeight);
760 rect.move(captionLogicalHeight, 0);
765 void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
767 if (!paintInfo.shouldPaintWithinRoot(*this))
770 LayoutRect rect(paintOffset, size());
771 subtractCaptionRect(rect);
773 BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context());
774 if (!boxShadowShouldBeAppliedToBackground(rect.location(), bleedAvoidance))
775 paintBoxShadow(paintInfo, rect, style(), Normal);
776 paintBackground(paintInfo, rect, bleedAvoidance);
777 paintBoxShadow(paintInfo, rect, style(), Inset);
779 if (style().hasVisibleBorderDecoration() && !collapseBorders())
780 paintBorder(paintInfo, rect, style());
783 void RenderTable::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
785 if (style().visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
788 LayoutRect rect(paintOffset, size());
789 subtractCaptionRect(rect);
791 paintMaskImages(paintInfo, rect);
794 void RenderTable::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) const
796 recalcSectionsIfNeeded();
797 // FIXME: Do the recalc in borderStart/borderEnd and make those const_cast this call.
798 // Then m_borderStart/m_borderEnd will be transparent a cache and it removes the possibility
799 // of reading out stale values.
800 const_cast<RenderTable*>(this)->recalcBordersInRowDirection();
801 // FIXME: Restructure the table layout code so that we can make this method const.
802 const_cast<RenderTable*>(this)->m_tableLayout->computeIntrinsicLogicalWidths(minWidth, maxWidth);
804 // FIXME: We should include captions widths here like we do in computePreferredLogicalWidths.
807 void RenderTable::computePreferredLogicalWidths()
809 ASSERT(preferredLogicalWidthsDirty());
811 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
813 LayoutUnit bordersPaddingAndSpacing = bordersPaddingAndSpacingInRowDirection();
814 m_minPreferredLogicalWidth += bordersPaddingAndSpacing;
815 m_maxPreferredLogicalWidth += bordersPaddingAndSpacing;
817 m_tableLayout->applyPreferredLogicalWidthQuirks(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
819 for (unsigned i = 0; i < m_captions.size(); i++)
820 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, m_captions[i]->minPreferredLogicalWidth());
822 auto& styleToUse = style();
823 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage or calc values for min-width.
824 if (styleToUse.logicalMinWidth().isFixed() && styleToUse.logicalMinWidth().value() > 0) {
825 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value()));
826 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value()));
829 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage or calc values for maxWidth.
830 if (styleToUse.logicalMaxWidth().isFixed()) {
831 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value()));
832 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value()));
835 // FIXME: We should be adding borderAndPaddingLogicalWidth here, but m_tableLayout->computePreferredLogicalWidths already does,
836 // so a bunch of tests break doing this naively.
837 setPreferredLogicalWidthsDirty(false);
840 RenderTableSection* RenderTable::topNonEmptySection() const
842 RenderTableSection* section = topSection();
843 if (section && !section->numRows())
844 section = sectionBelow(section, SkipEmptySections);
848 void RenderTable::splitColumn(unsigned position, unsigned firstSpan)
850 // We split the column at "position", taking "firstSpan" cells from the span.
851 ASSERT(m_columns[position].span > firstSpan);
852 m_columns.insert(position, ColumnStruct(firstSpan));
853 m_columns[position + 1].span -= firstSpan;
855 // Propagate the change in our columns representation to the sections that don't need
856 // cell recalc. If they do, they will be synced up directly with m_columns later.
857 for (auto& section : childrenOfType<RenderTableSection>(*this)) {
858 if (section.needsCellRecalc())
861 section.splitColumn(position, firstSpan);
864 m_columnPos.grow(numEffCols() + 1);
867 void RenderTable::appendColumn(unsigned span)
869 unsigned newColumnIndex = m_columns.size();
870 m_columns.append(ColumnStruct(span));
872 // Unless the table has cell(s) with colspan that exceed the number of columns afforded
873 // by the other rows in the table we can use the fast path when mapping columns to effective columns.
874 m_hasCellColspanThatDeterminesTableWidth = m_hasCellColspanThatDeterminesTableWidth || span > 1;
876 // Propagate the change in our columns representation to the sections that don't need
877 // cell recalc. If they do, they will be synced up directly with m_columns later.
878 for (auto& section : childrenOfType<RenderTableSection>(*this)) {
879 if (section.needsCellRecalc())
882 section.appendColumn(newColumnIndex);
885 m_columnPos.grow(numEffCols() + 1);
888 RenderTableCol* RenderTable::firstColumn() const
890 for (auto& child : childrenOfType<RenderObject>(*this)) {
891 if (is<RenderTableCol>(child))
892 return &const_cast<RenderTableCol&>(downcast<RenderTableCol>(child));
894 // We allow only table-captions before columns or column-groups.
895 if (!is<RenderTableCaption>(child))
902 void RenderTable::updateColumnCache() const
904 ASSERT(m_hasColElements);
905 ASSERT(m_columnRenderers.isEmpty());
906 ASSERT(m_effectiveColumnIndexMap.isEmpty());
907 ASSERT(!m_columnRenderersValid);
909 unsigned columnIndex = 0;
910 for (RenderTableCol* columnRenderer = firstColumn(); columnRenderer; columnRenderer = columnRenderer->nextColumn()) {
911 if (columnRenderer->isTableColumnGroupWithColumnChildren())
913 m_columnRenderers.append(columnRenderer);
914 // FIXME: We should look to compute the effective column index successively from previous values instead of
915 // calling colToEffCol(), which is in O(numEffCols()). Although it's unlikely that this is a hot function.
916 m_effectiveColumnIndexMap.add(columnRenderer, colToEffCol(columnIndex));
917 columnIndex += columnRenderer->span();
919 m_columnRenderersValid = true;
922 unsigned RenderTable::effectiveIndexOfColumn(const RenderTableCol& column) const
924 if (!m_columnRenderersValid)
926 const RenderTableCol* columnToUse = &column;
927 if (columnToUse->isTableColumnGroupWithColumnChildren())
928 columnToUse = columnToUse->nextColumn(); // First column in column-group
929 auto it = m_effectiveColumnIndexMap.find(columnToUse);
930 ASSERT(it != m_effectiveColumnIndexMap.end());
931 if (it == m_effectiveColumnIndexMap.end())
932 return std::numeric_limits<unsigned>::max();
936 LayoutUnit RenderTable::offsetTopForColumn(const RenderTableCol& column) const
938 if (effectiveIndexOfColumn(column) >= numEffCols())
940 if (m_columnOffsetTop >= 0) {
941 ASSERT(!needsLayout());
942 return m_columnOffsetTop;
944 RenderTableSection* section = topNonEmptySection();
945 return m_columnOffsetTop = section ? section->offsetTop() : LayoutUnit(0);
948 LayoutUnit RenderTable::offsetLeftForColumn(const RenderTableCol& column) const
950 unsigned columnIndex = effectiveIndexOfColumn(column);
951 if (columnIndex >= numEffCols())
953 return m_columnPos[columnIndex] + m_hSpacing + borderLeft();
956 LayoutUnit RenderTable::offsetWidthForColumn(const RenderTableCol& column) const
958 const RenderTableCol* currentColumn = &column;
959 bool hasColumnChildren;
960 if ((hasColumnChildren = currentColumn->isTableColumnGroupWithColumnChildren()))
961 currentColumn = currentColumn->nextColumn(); // First column in column-group
962 unsigned numberOfEffectiveColumns = numEffCols();
963 ASSERT_WITH_SECURITY_IMPLICATION(m_columnPos.size() >= numberOfEffectiveColumns + 1);
964 LayoutUnit width = 0;
965 LayoutUnit spacing = m_hSpacing;
966 while (currentColumn) {
967 unsigned columnIndex = effectiveIndexOfColumn(*currentColumn);
968 unsigned span = currentColumn->span();
969 while (span && columnIndex < numberOfEffectiveColumns) {
970 width += m_columnPos[columnIndex + 1] - m_columnPos[columnIndex] - spacing;
971 span -= m_columns[columnIndex].span;
976 if (!hasColumnChildren)
978 currentColumn = currentColumn->nextColumn();
979 if (!currentColumn || currentColumn->isTableColumnGroup())
986 LayoutUnit RenderTable::offsetHeightForColumn(const RenderTableCol& column) const
988 if (effectiveIndexOfColumn(column) >= numEffCols())
990 if (m_columnOffsetHeight >= 0) {
991 ASSERT(!needsLayout());
992 return m_columnOffsetHeight;
994 LayoutUnit height = 0;
995 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section))
996 height += section->offsetHeight();
997 m_columnOffsetHeight = height;
998 return m_columnOffsetHeight;
1001 RenderTableCol* RenderTable::slowColElement(unsigned col, bool* startEdge, bool* endEdge) const
1003 ASSERT(m_hasColElements);
1005 if (!m_columnRenderersValid)
1006 updateColumnCache();
1008 unsigned columnCount = 0;
1009 for (unsigned i = 0; i < m_columnRenderers.size(); i++) {
1010 RenderTableCol* columnRenderer = m_columnRenderers[i];
1011 unsigned span = columnRenderer->span();
1012 unsigned startCol = columnCount;
1014 unsigned endCol = columnCount + span - 1;
1015 columnCount += span;
1016 if (columnCount > col) {
1018 *startEdge = startCol == col;
1020 *endEdge = endCol == col;
1021 return columnRenderer;
1027 void RenderTable::recalcSections() const
1029 ASSERT(m_needsSectionRecalc);
1034 m_hasColElements = false;
1035 m_hasCellColspanThatDeterminesTableWidth = hasCellColspanThatDeterminesTableWidth();
1037 // We need to get valid pointers to caption, head, foot and first body again
1038 RenderObject* nextSibling;
1039 for (RenderObject* child = firstChild(); child; child = nextSibling) {
1040 nextSibling = child->nextSibling();
1041 switch (child->style().display()) {
1043 case TABLE_COLUMN_GROUP:
1044 m_hasColElements = true;
1046 case TABLE_HEADER_GROUP:
1047 if (is<RenderTableSection>(*child)) {
1048 RenderTableSection& section = downcast<RenderTableSection>(*child);
1051 else if (!m_firstBody)
1052 m_firstBody = §ion;
1053 section.recalcCellsIfNeeded();
1056 case TABLE_FOOTER_GROUP:
1057 if (is<RenderTableSection>(*child)) {
1058 RenderTableSection& section = downcast<RenderTableSection>(*child);
1061 else if (!m_firstBody)
1062 m_firstBody = §ion;
1063 section.recalcCellsIfNeeded();
1066 case TABLE_ROW_GROUP:
1067 if (is<RenderTableSection>(*child)) {
1068 RenderTableSection& section = downcast<RenderTableSection>(*child);
1070 m_firstBody = §ion;
1071 section.recalcCellsIfNeeded();
1079 // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section)
1080 unsigned maxCols = 0;
1081 for (auto& section : childrenOfType<RenderTableSection>(*this)) {
1082 unsigned sectionCols = section.numColumns();
1083 if (sectionCols > maxCols)
1084 maxCols = sectionCols;
1087 m_columns.resize(maxCols);
1088 m_columnPos.resize(maxCols + 1);
1090 ASSERT(selfNeedsLayout());
1092 m_needsSectionRecalc = false;
1095 LayoutUnit RenderTable::calcBorderStart() const
1097 if (!collapseBorders())
1098 return RenderBlock::borderStart();
1100 // Determined by the first cell of the first row. See the CSS 2.1 spec, section 17.6.2.
1104 float borderWidth = 0;
1106 const BorderValue& tableStartBorder = style().borderStart();
1107 if (tableStartBorder.style() == BHIDDEN)
1109 if (tableStartBorder.style() > BHIDDEN)
1110 borderWidth = tableStartBorder.width();
1112 if (RenderTableCol* column = colElement(0)) {
1113 // FIXME: We don't account for direction on columns and column groups.
1114 const BorderValue& columnAdjoiningBorder = column->style().borderStart();
1115 if (columnAdjoiningBorder.style() == BHIDDEN)
1117 if (columnAdjoiningBorder.style() > BHIDDEN)
1118 borderWidth = std::max(borderWidth, columnAdjoiningBorder.width());
1119 // FIXME: This logic doesn't properly account for the first column in the first column-group case.
1122 if (const RenderTableSection* topNonEmptySection = this->topNonEmptySection()) {
1123 const BorderValue& sectionAdjoiningBorder = topNonEmptySection->borderAdjoiningTableStart();
1124 if (sectionAdjoiningBorder.style() == BHIDDEN)
1127 if (sectionAdjoiningBorder.style() > BHIDDEN)
1128 borderWidth = std::max(borderWidth, sectionAdjoiningBorder.width());
1130 if (const RenderTableCell* adjoiningStartCell = topNonEmptySection->firstRowCellAdjoiningTableStart()) {
1131 // FIXME: Make this work with perpendicular and flipped cells.
1132 const BorderValue& startCellAdjoiningBorder = adjoiningStartCell->borderAdjoiningTableStart();
1133 if (startCellAdjoiningBorder.style() == BHIDDEN)
1136 const BorderValue& firstRowAdjoiningBorder = adjoiningStartCell->row()->borderAdjoiningTableStart();
1137 if (firstRowAdjoiningBorder.style() == BHIDDEN)
1140 if (startCellAdjoiningBorder.style() > BHIDDEN)
1141 borderWidth = std::max(borderWidth, startCellAdjoiningBorder.width());
1142 if (firstRowAdjoiningBorder.style() > BHIDDEN)
1143 borderWidth = std::max(borderWidth, firstRowAdjoiningBorder.width());
1146 return CollapsedBorderValue::adjustedCollapsedBorderWidth(borderWidth, document().deviceScaleFactor(), !style().isLeftToRightDirection());
1149 LayoutUnit RenderTable::calcBorderEnd() const
1151 if (!collapseBorders())
1152 return RenderBlock::borderEnd();
1154 // Determined by the last cell of the first row. See the CSS 2.1 spec, section 17.6.2.
1158 float borderWidth = 0;
1160 const BorderValue& tableEndBorder = style().borderEnd();
1161 if (tableEndBorder.style() == BHIDDEN)
1163 if (tableEndBorder.style() > BHIDDEN)
1164 borderWidth = tableEndBorder.width();
1166 unsigned endColumn = numEffCols() - 1;
1167 if (RenderTableCol* column = colElement(endColumn)) {
1168 // FIXME: We don't account for direction on columns and column groups.
1169 const BorderValue& columnAdjoiningBorder = column->style().borderEnd();
1170 if (columnAdjoiningBorder.style() == BHIDDEN)
1172 if (columnAdjoiningBorder.style() > BHIDDEN)
1173 borderWidth = std::max(borderWidth, columnAdjoiningBorder.width());
1174 // FIXME: This logic doesn't properly account for the last column in the last column-group case.
1177 if (const RenderTableSection* topNonEmptySection = this->topNonEmptySection()) {
1178 const BorderValue& sectionAdjoiningBorder = topNonEmptySection->borderAdjoiningTableEnd();
1179 if (sectionAdjoiningBorder.style() == BHIDDEN)
1182 if (sectionAdjoiningBorder.style() > BHIDDEN)
1183 borderWidth = std::max(borderWidth, sectionAdjoiningBorder.width());
1185 if (const RenderTableCell* adjoiningEndCell = topNonEmptySection->firstRowCellAdjoiningTableEnd()) {
1186 // FIXME: Make this work with perpendicular and flipped cells.
1187 const BorderValue& endCellAdjoiningBorder = adjoiningEndCell->borderAdjoiningTableEnd();
1188 if (endCellAdjoiningBorder.style() == BHIDDEN)
1191 const BorderValue& firstRowAdjoiningBorder = adjoiningEndCell->row()->borderAdjoiningTableEnd();
1192 if (firstRowAdjoiningBorder.style() == BHIDDEN)
1195 if (endCellAdjoiningBorder.style() > BHIDDEN)
1196 borderWidth = std::max(borderWidth, endCellAdjoiningBorder.width());
1197 if (firstRowAdjoiningBorder.style() > BHIDDEN)
1198 borderWidth = std::max(borderWidth, firstRowAdjoiningBorder.width());
1201 return CollapsedBorderValue::adjustedCollapsedBorderWidth(borderWidth, document().deviceScaleFactor(), style().isLeftToRightDirection());
1204 void RenderTable::recalcBordersInRowDirection()
1206 // FIXME: We need to compute the collapsed before / after borders in the same fashion.
1207 m_borderStart = calcBorderStart();
1208 m_borderEnd = calcBorderEnd();
1211 LayoutUnit RenderTable::borderBefore() const
1213 if (collapseBorders()) {
1214 recalcSectionsIfNeeded();
1215 return outerBorderBefore();
1217 return RenderBlock::borderBefore();
1220 LayoutUnit RenderTable::borderAfter() const
1222 if (collapseBorders()) {
1223 recalcSectionsIfNeeded();
1224 return outerBorderAfter();
1226 return RenderBlock::borderAfter();
1229 LayoutUnit RenderTable::outerBorderBefore() const
1231 if (!collapseBorders())
1233 LayoutUnit borderWidth = 0;
1234 if (RenderTableSection* topSection = this->topSection()) {
1235 borderWidth = topSection->outerBorderBefore();
1236 if (borderWidth < 0)
1237 return 0; // Overridden by hidden
1239 const BorderValue& tb = style().borderBefore();
1240 if (tb.style() == BHIDDEN)
1242 if (tb.style() > BHIDDEN) {
1243 LayoutUnit collapsedBorderWidth = std::max<LayoutUnit>(borderWidth, tb.width() / 2);
1244 borderWidth = floorToDevicePixel(collapsedBorderWidth, document().deviceScaleFactor());
1249 LayoutUnit RenderTable::outerBorderAfter() const
1251 if (!collapseBorders())
1253 LayoutUnit borderWidth = 0;
1255 if (RenderTableSection* section = bottomSection()) {
1256 borderWidth = section->outerBorderAfter();
1257 if (borderWidth < 0)
1258 return 0; // Overridden by hidden
1260 const BorderValue& tb = style().borderAfter();
1261 if (tb.style() == BHIDDEN)
1263 if (tb.style() > BHIDDEN) {
1264 float deviceScaleFactor = document().deviceScaleFactor();
1265 LayoutUnit collapsedBorderWidth = std::max<LayoutUnit>(borderWidth, (tb.width() + (1 / deviceScaleFactor)) / 2);
1266 borderWidth = floorToDevicePixel(collapsedBorderWidth, deviceScaleFactor);
1271 LayoutUnit RenderTable::outerBorderStart() const
1273 if (!collapseBorders())
1276 LayoutUnit borderWidth = 0;
1278 const BorderValue& tb = style().borderStart();
1279 if (tb.style() == BHIDDEN)
1281 if (tb.style() > BHIDDEN)
1282 return CollapsedBorderValue::adjustedCollapsedBorderWidth(tb.width(), document().deviceScaleFactor(), !style().isLeftToRightDirection());
1284 bool allHidden = true;
1285 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) {
1286 LayoutUnit sw = section->outerBorderStart();
1290 borderWidth = std::max(borderWidth, sw);
1298 LayoutUnit RenderTable::outerBorderEnd() const
1300 if (!collapseBorders())
1303 LayoutUnit borderWidth = 0;
1305 const BorderValue& tb = style().borderEnd();
1306 if (tb.style() == BHIDDEN)
1308 if (tb.style() > BHIDDEN)
1309 return CollapsedBorderValue::adjustedCollapsedBorderWidth(tb.width(), document().deviceScaleFactor(), style().isLeftToRightDirection());
1311 bool allHidden = true;
1312 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) {
1313 LayoutUnit sw = section->outerBorderEnd();
1317 borderWidth = std::max(borderWidth, sw);
1325 RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const
1327 recalcSectionsIfNeeded();
1329 if (section == m_head)
1332 RenderObject* prevSection = section == m_foot ? lastChild() : section->previousSibling();
1333 while (prevSection) {
1334 if (is<RenderTableSection>(*prevSection) && prevSection != m_head && prevSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || downcast<RenderTableSection>(*prevSection).numRows()))
1336 prevSection = prevSection->previousSibling();
1338 if (!prevSection && m_head && (skipEmptySections == DoNotSkipEmptySections || m_head->numRows()))
1339 prevSection = m_head;
1340 return downcast<RenderTableSection>(prevSection);
1343 RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const
1345 recalcSectionsIfNeeded();
1347 if (section == m_foot)
1350 RenderObject* nextSection = section == m_head ? firstChild() : section->nextSibling();
1351 while (nextSection) {
1352 if (is<RenderTableSection>(*nextSection) && nextSection != m_head && nextSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || downcast<RenderTableSection>(*nextSection).numRows()))
1354 nextSection = nextSection->nextSibling();
1356 if (!nextSection && m_foot && (skipEmptySections == DoNotSkipEmptySections || m_foot->numRows()))
1357 nextSection = m_foot;
1358 return downcast<RenderTableSection>(nextSection);
1361 RenderTableSection* RenderTable::bottomSection() const
1363 recalcSectionsIfNeeded();
1368 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1369 if (is<RenderTableSection>(*child))
1370 return downcast<RenderTableSection>(child);
1376 RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
1378 recalcSectionsIfNeeded();
1380 // Find the section and row to look in
1381 unsigned r = cell->rowIndex();
1382 RenderTableSection* section = nullptr;
1383 unsigned rAbove = 0;
1385 // cell is not in the first row, so use the above row in its own section
1386 section = cell->section();
1389 section = sectionAbove(cell->section(), SkipEmptySections);
1391 ASSERT(section->numRows());
1392 rAbove = section->numRows() - 1;
1396 // Look up the cell in the section's grid, which requires effective col index
1398 unsigned effCol = colToEffCol(cell->col());
1399 RenderTableSection::CellStruct& aboveCell = section->cellAt(rAbove, effCol);
1400 return aboveCell.primaryCell();
1405 RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const
1407 recalcSectionsIfNeeded();
1409 // Find the section and row to look in
1410 unsigned r = cell->rowIndex() + cell->rowSpan() - 1;
1411 RenderTableSection* section = nullptr;
1412 unsigned rBelow = 0;
1413 if (r < cell->section()->numRows() - 1) {
1414 // The cell is not in the last row, so use the next row in the section.
1415 section = cell->section();
1418 section = sectionBelow(cell->section(), SkipEmptySections);
1423 // Look up the cell in the section's grid, which requires effective col index
1425 unsigned effCol = colToEffCol(cell->col());
1426 RenderTableSection::CellStruct& belowCell = section->cellAt(rBelow, effCol);
1427 return belowCell.primaryCell();
1432 RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const
1434 recalcSectionsIfNeeded();
1436 RenderTableSection* section = cell->section();
1437 unsigned effCol = colToEffCol(cell->col());
1441 // If we hit a colspan back up to a real cell.
1442 RenderTableSection::CellStruct& prevCell = section->cellAt(cell->rowIndex(), effCol - 1);
1443 return prevCell.primaryCell();
1446 RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const
1448 recalcSectionsIfNeeded();
1450 unsigned effCol = colToEffCol(cell->col() + cell->colSpan());
1451 if (effCol >= numEffCols())
1453 return cell->section()->primaryCellAt(cell->rowIndex(), effCol);
1456 RenderBlock* RenderTable::firstLineBlock() const
1461 void RenderTable::updateFirstLetter()
1465 int RenderTable::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1467 return firstLineBaseline().valueOrCompute([&] {
1468 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1472 Optional<int> RenderTable::inlineBlockBaseline(LineDirectionMode) const
1474 // Tables are skipped when computing an inline-block's baseline.
1475 return Optional<int>();
1478 Optional<int> RenderTable::firstLineBaseline() const
1480 // The baseline of a 'table' is the same as the 'inline-table' baseline per CSS 3 Flexbox (CSS 2.1
1481 // doesn't define the baseline of a 'table' only an 'inline-table').
1482 // This is also needed to properly determine the baseline of a cell if it has a table child.
1484 if (isWritingModeRoot())
1485 return Optional<int>();
1487 recalcSectionsIfNeeded();
1489 const RenderTableSection* topNonEmptySection = this->topNonEmptySection();
1490 if (!topNonEmptySection)
1491 return Optional<int>();
1493 if (Optional<int> baseline = topNonEmptySection->firstLineBaseline())
1494 return Optional<int>(topNonEmptySection->logicalTop() + baseline.value());
1496 // FIXME: A table row always has a baseline per CSS 2.1. Will this return the right value?
1497 return Optional<int>();
1500 LayoutRect RenderTable::overflowClipRect(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy, PaintPhase phase)
1503 // Don't clip out the table's side of the collapsed borders if we're in the paint phase that will ask the sections to paint them.
1504 // Likewise, if we're self-painting we avoid clipping them out as the clip rect that will be passed down to child layers from RenderLayer will do that instead.
1505 if (phase == PaintPhaseChildBlockBackgrounds || layer()->isSelfPaintingLayer()) {
1506 rect = borderBoxRectInRegion(region);
1507 rect.setLocation(location + rect.location());
1509 rect = RenderBox::overflowClipRect(location, region, relevancy);
1511 // If we have a caption, expand the clip to include the caption.
1512 // FIXME: Technically this is wrong, but it's virtually impossible to fix this
1513 // for real until captions have been re-written.
1514 // FIXME: This code assumes (like all our other caption code) that only top/bottom are
1515 // supported. When we actually support left/right and stop mapping them to top/bottom,
1516 // we might have to hack this code first (depending on what order we do these bug fixes in).
1517 if (!m_captions.isEmpty()) {
1518 if (style().isHorizontalWritingMode()) {
1519 rect.setHeight(height());
1520 rect.setY(location.y());
1522 rect.setWidth(width());
1523 rect.setX(location.x());
1530 bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1532 LayoutPoint adjustedLocation = accumulatedOffset + location();
1534 // Check kids first.
1535 if (!hasOverflowClip() || locationInContainer.intersects(overflowClipRect(adjustedLocation, currentRenderNamedFlowFragment()))) {
1536 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1537 if (is<RenderBox>(*child) && !downcast<RenderBox>(*child).hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) {
1538 LayoutPoint childPoint = flipForWritingModeForChild(downcast<RenderBox>(child), adjustedLocation);
1539 if (child->nodeAtPoint(request, result, locationInContainer, childPoint, action)) {
1540 updateHitTestResult(result, toLayoutPoint(locationInContainer.point() - childPoint));
1547 // Check our bounds next.
1548 LayoutRect boundsRect(adjustedLocation, size());
1549 if (visibleToHitTesting() && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && locationInContainer.intersects(boundsRect)) {
1550 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(adjustedLocation)));
1551 if (!result.addNodeToRectBasedTestResult(element(), request, locationInContainer, boundsRect))
1558 RenderTable* RenderTable::createAnonymousWithParentRenderer(const RenderObject* parent)
1560 auto table = new RenderTable(parent->document(), RenderStyle::createAnonymousStyleWithDisplay(parent->style(), parent->style().display() == INLINE ? INLINE_TABLE : TABLE));
1561 table->initializeStyle();
1565 const BorderValue& RenderTable::tableStartBorderAdjoiningCell(const RenderTableCell& cell) const
1567 ASSERT(cell.isFirstOrLastCellInRow());
1568 if (isDirectionSame(this, cell.row()))
1569 return style().borderStart();
1571 return style().borderEnd();
1574 const BorderValue& RenderTable::tableEndBorderAdjoiningCell(const RenderTableCell& cell) const
1576 ASSERT(cell.isFirstOrLastCellInRow());
1577 if (isDirectionSame(this, cell.row()))
1578 return style().borderEnd();
1580 return style().borderStart();
1583 void RenderTable::markForPaginationRelayoutIfNeeded()
1585 if (!view().layoutState()->isPaginated() || (!view().layoutState()->pageLogicalHeightChanged() && (!view().layoutState()->pageLogicalHeight() || view().layoutState()->pageLogicalOffset(this, logicalTop()) == pageLogicalOffset())))
1588 // When a table moves, we have to dirty all of the sections too.
1590 setChildNeedsLayout(MarkOnlyThis);
1591 for (auto& child : childrenOfType<RenderTableSection>(*this)) {
1592 if (!child.needsLayout())
1593 child.setChildNeedsLayout(MarkOnlyThis);