| /* |
| * Copyright (C) 1997 Martin Jones (mjones@kde.org) |
| * (C) 1997 Torben Weis (weis@kde.org) |
| * (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, 2013 Apple Inc. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "core/layout/LayoutTableRow.h" |
| |
| #include "core/HTMLNames.h" |
| #include "core/fetch/ImageResource.h" |
| #include "core/layout/HitTestResult.h" |
| #include "core/layout/LayoutAnalyzer.h" |
| #include "core/layout/LayoutState.h" |
| #include "core/layout/LayoutTableCell.h" |
| #include "core/layout/SubtreeLayoutScope.h" |
| #include "core/paint/TableRowPainter.h" |
| #include "core/style/StyleInheritedData.h" |
| |
| namespace blink { |
| |
| using namespace HTMLNames; |
| |
| LayoutTableRow::LayoutTableRow(Element* element) |
| : LayoutTableBoxComponent(element) |
| , m_rowIndex(unsetRowIndex) |
| { |
| // init LayoutObject attributes |
| setInline(false); // our object is not Inline |
| } |
| |
| void LayoutTableRow::willBeRemovedFromTree() |
| { |
| LayoutTableBoxComponent::willBeRemovedFromTree(); |
| |
| section()->setNeedsCellRecalc(); |
| } |
| |
| void LayoutTableRow::styleDidChange(StyleDifference diff, const ComputedStyle* oldStyle) |
| { |
| DCHECK_EQ(style()->display(), EDisplay::TableRow); |
| |
| LayoutTableBoxComponent::styleDidChange(diff, oldStyle); |
| propagateStyleToAnonymousChildren(); |
| |
| if (!oldStyle) |
| return; |
| |
| if (section() && style()->logicalHeight() != oldStyle->logicalHeight()) |
| section()->rowLogicalHeightChanged(this); |
| |
| if (!parent()) |
| return; |
| LayoutTable* table = this->table(); |
| if (!table) |
| return; |
| |
| if (!table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle->border() != style()->border()) |
| table->invalidateCollapsedBorders(); |
| |
| if (LayoutTableBoxComponent::doCellsHaveDirtyWidth(*this, *table, diff, *oldStyle)) { |
| // If the border width changes on a row, we need to make sure the cells in the row know to lay out again. |
| // This only happens when borders are collapsed, since they end up affecting the border sides of the cell |
| // itself. |
| for (LayoutBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { |
| if (!childBox->isTableCell()) |
| continue; |
| // TODO(dgrogan) Add a layout test showing that setChildNeedsLayout is needed instead of setNeedsLayout. |
| childBox->setChildNeedsLayout(); |
| childBox->setPreferredLogicalWidthsDirty(MarkOnlyThis); |
| } |
| // Most table componenents can rely on LayoutObject::styleDidChange |
| // to mark the container chain dirty. But LayoutTableSection seems |
| // to never clear its dirty bit, which stops the propagation. So |
| // anything under LayoutTableSection has to restart the propagation |
| // at the table. |
| // TODO(dgrogan): Make LayoutTableSection clear its dirty bit. |
| table->setPreferredLogicalWidthsDirty(); |
| } |
| } |
| |
| const BorderValue& LayoutTableRow::borderAdjoiningStartCell(const LayoutTableCell* cell) const |
| { |
| ASSERT_UNUSED(cell, cell->isFirstOrLastCellInRow()); |
| // FIXME: https://webkit.org/b/79272 - Add support for mixed directionality at the cell level. |
| return style()->borderStart(); |
| } |
| |
| const BorderValue& LayoutTableRow::borderAdjoiningEndCell(const LayoutTableCell* cell) const |
| { |
| ASSERT_UNUSED(cell, cell->isFirstOrLastCellInRow()); |
| // FIXME: https://webkit.org/b/79272 - Add support for mixed directionality at the cell level. |
| return style()->borderEnd(); |
| } |
| |
| void LayoutTableRow::addChild(LayoutObject* child, LayoutObject* beforeChild) |
| { |
| if (!child->isTableCell()) { |
| LayoutObject* last = beforeChild; |
| if (!last) |
| last = lastCell(); |
| if (last && last->isAnonymous() && last->isTableCell() && !last->isBeforeOrAfterContent()) { |
| LayoutTableCell* lastCell = toLayoutTableCell(last); |
| if (beforeChild == lastCell) |
| beforeChild = lastCell->firstChild(); |
| lastCell->addChild(child, beforeChild); |
| return; |
| } |
| |
| if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) { |
| LayoutObject* cell = beforeChild->previousSibling(); |
| if (cell && cell->isTableCell() && cell->isAnonymous()) { |
| cell->addChild(child); |
| return; |
| } |
| } |
| |
| // If beforeChild is inside an anonymous cell, insert into the cell. |
| if (last && !last->isTableCell() && last->parent() && last->parent()->isAnonymous() && !last->parent()->isBeforeOrAfterContent()) { |
| last->parent()->addChild(child, beforeChild); |
| return; |
| } |
| |
| LayoutTableCell* cell = LayoutTableCell::createAnonymousWithParent(this); |
| addChild(cell, beforeChild); |
| cell->addChild(child); |
| return; |
| } |
| |
| if (beforeChild && beforeChild->parent() != this) |
| beforeChild = splitAnonymousBoxesAroundChild(beforeChild); |
| |
| LayoutTableCell* cell = toLayoutTableCell(child); |
| |
| ASSERT(!beforeChild || beforeChild->isTableCell()); |
| LayoutTableBoxComponent::addChild(cell, beforeChild); |
| |
| // Generated content can result in us having a null section so make sure to null check our parent. |
| if (parent()) { |
| section()->addCell(cell, this); |
| // When borders collapse, adding a cell can affect the the width of neighboring cells. |
| LayoutTable* enclosingTable = table(); |
| if (enclosingTable && enclosingTable->collapseBorders()) { |
| if (LayoutTableCell* previousCell = cell->previousCell()) |
| previousCell->setNeedsLayoutAndPrefWidthsRecalc(LayoutInvalidationReason::TableChanged); |
| if (LayoutTableCell* nextCell = cell->nextCell()) |
| nextCell->setNeedsLayoutAndPrefWidthsRecalc(LayoutInvalidationReason::TableChanged); |
| } |
| } |
| |
| if (beforeChild || nextRow()) |
| section()->setNeedsCellRecalc(); |
| } |
| |
| void LayoutTableRow::layout() |
| { |
| ASSERT(needsLayout()); |
| LayoutAnalyzer::Scope analyzer(*this); |
| |
| // Table rows do not add translation. |
| LayoutState state(*this, LayoutSize()); |
| |
| for (LayoutTableCell* cell = firstCell(); cell; cell = cell->nextCell()) { |
| SubtreeLayoutScope layouter(*cell); |
| if (!cell->needsLayout()) |
| markChildForPaginationRelayoutIfNeeded(*cell, layouter); |
| if (cell->needsLayout()) |
| cell->layout(); |
| // We're laying out each cell here to establish its raw logical height so it can be used to |
| // figure out the row's height and baseline later on in layoutRows(). As part of that we |
| // will layout the cell again if we're in a paginated context and come up with the |
| // correct strut. Any strut we come up with here will depend on the old paged layout and will |
| // give the cell an invalid height that is not useful for figuring out the raw height of the row. |
| if (cell->firstRootBox() && cell->firstRootBox()->paginationStrut()) |
| cell->setLogicalHeight(cell->logicalHeight() - cell->firstRootBox()->paginationStrut()); |
| } |
| |
| m_overflow.reset(); |
| addVisualEffectOverflow(); |
| // We do not call addOverflowFromCell here. The cell are laid out to be |
| // measured above and will be sized correctly in a follow-up phase. |
| |
| // We only ever need to issue paint invalidations if our cells didn't, which means that they didn't need |
| // layout, so we know that our bounds didn't change. This code is just making up for |
| // the fact that we did not invalidate paints in setStyle() because we had a layout hint. |
| if (selfNeedsLayout()) { |
| for (LayoutTableCell* cell = firstCell(); cell; cell = cell->nextCell()) { |
| // FIXME: Is this needed when issuing paint invalidations after layout? |
| cell->setShouldDoFullPaintInvalidation(); |
| } |
| } |
| |
| // LayoutTableSection::layoutRows will set our logical height and width later, so it calls updateLayerTransform(). |
| clearNeedsLayout(); |
| } |
| |
| // Hit Testing |
| bool LayoutTableRow::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) |
| { |
| // Table rows cannot ever be hit tested. Effectively they do not exist. |
| // Just forward to our children always. |
| for (LayoutTableCell* cell = lastCell(); cell; cell = cell->previousCell()) { |
| // FIXME: We have to skip over inline flows, since they can show up inside table rows |
| // at the moment (a demoted inline <form> for example). If we ever implement a |
| // table-specific hit-test method (which we should do for performance reasons anyway), |
| // then we can remove this check. |
| if (!cell->hasSelfPaintingLayer()) { |
| LayoutPoint cellPoint = flipForWritingModeForChild(cell, accumulatedOffset); |
| if (cell->nodeAtPoint(result, locationInContainer, cellPoint, action)) { |
| updateHitTestResult(result, locationInContainer.point() - toLayoutSize(cellPoint)); |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| void LayoutTableRow::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) const |
| { |
| TableRowPainter(*this).paint(paintInfo, paintOffset); |
| } |
| |
| LayoutTableRow* LayoutTableRow::createAnonymous(Document* document) |
| { |
| LayoutTableRow* layoutObject = new LayoutTableRow(nullptr); |
| layoutObject->setDocumentForAnonymous(document); |
| return layoutObject; |
| } |
| |
| LayoutTableRow* LayoutTableRow::createAnonymousWithParent(const LayoutObject* parent) |
| { |
| LayoutTableRow* newRow = LayoutTableRow::createAnonymous(&parent->document()); |
| RefPtr<ComputedStyle> newStyle = ComputedStyle::createAnonymousStyleWithDisplay(parent->styleRef(), EDisplay::TableRow); |
| newRow->setStyle(newStyle.release()); |
| return newRow; |
| } |
| |
| void LayoutTableRow::computeOverflow() |
| { |
| clearAllOverflows(); |
| addVisualEffectOverflow(); |
| for (LayoutTableCell* cell = firstCell(); cell; cell = cell->nextCell()) |
| addOverflowFromCell(cell); |
| } |
| |
| void LayoutTableRow::addOverflowFromCell(const LayoutTableCell* cell) |
| { |
| // Non-row-spanning-cells don't create overflow (they are fully contained within this row). |
| // TODO(crbug.com/603993): This seems incorrect because cell may have visual effect overflow that should be included in this row. |
| if (cell->rowSpan() == 1) |
| return; |
| |
| // Cells only generates visual overflow. |
| LayoutRect cellVisualOverflowRect = cell->visualOverflowRectForPropagation(styleRef()); |
| |
| // The cell and the row share the section's coordinate system. However |
| // the visual overflow should be determined in the coordinate system of |
| // the row, that's why we shift it below. |
| LayoutUnit cellOffsetLogicalTopDifference = cell->location().y() - location().y(); |
| cellVisualOverflowRect.move(LayoutUnit(), cellOffsetLogicalTopDifference); |
| |
| addContentsVisualOverflow(cellVisualOverflowRect); |
| } |
| |
| } // namespace blink |