| /* |
| * 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, 2009, 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. |
| */ |
| |
| #ifndef LayoutTableCell_h |
| #define LayoutTableCell_h |
| |
| #include "core/CoreExport.h" |
| #include "core/layout/LayoutBlockFlow.h" |
| #include "core/layout/LayoutTableRow.h" |
| #include "core/layout/LayoutTableSection.h" |
| #include "platform/LengthFunctions.h" |
| #include <memory> |
| |
| namespace blink { |
| |
| static const unsigned unsetColumnIndex = 0x1FFFFFFF; |
| static const unsigned maxColumnIndex = 0x1FFFFFFE; // 536,870,910 |
| |
| enum IncludeBorderColorOrNot { DoNotIncludeBorderColor, IncludeBorderColor }; |
| |
| class SubtreeLayoutScope; |
| |
| // LayoutTableCell is used to represent a table cell (display: table-cell). |
| // |
| // Because rows are as tall as the tallest cell, cells need to be aligned into |
| // the enclosing row space. To achieve this, LayoutTableCell introduces the |
| // concept of 'intrinsic padding'. Those 2 paddings are used to shift the box |
| // into the row as follows: |
| // |
| // -------------------------------- |
| // ^ ^ |
| // | | |
| // | | cell's border before |
| // | | |
| // | v |
| // | ^ |
| // | | |
| // | | m_intrinsicPaddingBefore |
| // | | |
| // | v |
| // | ----------------------------- |
| // | | | |
| // row | | cell's padding box | |
| // height | | | |
| // | ----------------------------- |
| // | ^ |
| // | | |
| // | | m_intrinsicPaddingAfter |
| // | | |
| // | v |
| // | ^ |
| // | | |
| // | | cell's border after |
| // | | |
| // v v |
| // --------------------------------- |
| // |
| // Note that this diagram is not impacted by collapsing or separate borders |
| // (see 'border-collapse'). |
| // Also there is no margin on table cell (or any internal table element). |
| // |
| // LayoutTableCell is positioned with respect to the enclosing |
| // LayoutTableSection. See callers of |
| // LayoutTableSection::setLogicalPositionForCell() for when it is placed. |
| class CORE_EXPORT LayoutTableCell final : public LayoutBlockFlow { |
| public: |
| explicit LayoutTableCell(Element*); |
| |
| unsigned colSpan() const { |
| if (!m_hasColSpan) |
| return 1; |
| return parseColSpanFromDOM(); |
| } |
| unsigned rowSpan() const { |
| if (!m_hasRowSpan) |
| return 1; |
| return parseRowSpanFromDOM(); |
| } |
| |
| // Called from HTMLTableCellElement. |
| void colSpanOrRowSpanChanged(); |
| |
| void setAbsoluteColumnIndex(unsigned column) { |
| if (UNLIKELY(column > maxColumnIndex)) |
| CRASH(); |
| |
| m_absoluteColumnIndex = column; |
| } |
| |
| bool hasSetAbsoluteColumnIndex() const { |
| return m_absoluteColumnIndex != unsetColumnIndex; |
| } |
| |
| unsigned absoluteColumnIndex() const { |
| ASSERT(hasSetAbsoluteColumnIndex()); |
| return m_absoluteColumnIndex; |
| } |
| |
| LayoutTableRow* row() const { return toLayoutTableRow(parent()); } |
| LayoutTableSection* section() const { |
| return toLayoutTableSection(parent()->parent()); |
| } |
| LayoutTable* table() const { |
| return toLayoutTable(parent()->parent()->parent()); |
| } |
| |
| LayoutTableCell* previousCell() const; |
| LayoutTableCell* nextCell() const; |
| |
| unsigned rowIndex() const { |
| // This function shouldn't be called on a detached cell. |
| ASSERT(row()); |
| return row()->rowIndex(); |
| } |
| |
| Length styleOrColLogicalWidth() const { |
| Length styleWidth = style()->logicalWidth(); |
| if (!styleWidth.isAuto()) |
| return styleWidth; |
| if (LayoutTableCol* firstColumn = |
| table() |
| ->colElementAtAbsoluteColumn(absoluteColumnIndex()) |
| .innermostColOrColGroup()) |
| return logicalWidthFromColumns(firstColumn, styleWidth); |
| return styleWidth; |
| } |
| |
| int logicalHeightFromStyle() const { |
| Length height = style()->logicalHeight(); |
| int styleLogicalHeight = height.isIntrinsicOrAuto() |
| ? 0 |
| : valueForLength(height, LayoutUnit()).toInt(); |
| |
| // In strict mode, box-sizing: content-box do the right thing and actually |
| // add in the border and padding. |
| // Call computedCSSPadding* directly to avoid including implicitPadding. |
| if (!document().inQuirksMode() && |
| style()->boxSizing() != EBoxSizing::kBorderBox) |
| styleLogicalHeight += |
| (computedCSSPaddingBefore() + computedCSSPaddingAfter()).floor() + |
| borderBefore() + borderAfter(); |
| return styleLogicalHeight; |
| } |
| |
| int logicalHeightForRowSizing() const { |
| // FIXME: This function does too much work, and is very hot during table |
| // layout! |
| int adjustedLogicalHeight = |
| pixelSnappedLogicalHeight() - |
| (intrinsicPaddingBefore() + intrinsicPaddingAfter()); |
| int styleLogicalHeight = logicalHeightFromStyle(); |
| return max(styleLogicalHeight, adjustedLogicalHeight); |
| } |
| |
| void setCellLogicalWidth(int constrainedLogicalWidth, SubtreeLayoutScope&); |
| |
| int borderLeft() const override; |
| int borderRight() const override; |
| int borderTop() const override; |
| int borderBottom() const override; |
| int borderStart() const override; |
| int borderEnd() const override; |
| int borderBefore() const override; |
| int borderAfter() const override; |
| |
| void collectBorderValues(LayoutTable::CollapsedBorderValues&); |
| static void sortBorderValues(LayoutTable::CollapsedBorderValues&); |
| |
| void layout() override; |
| |
| void paint(const PaintInfo&, const LayoutPoint&) const override; |
| |
| int cellBaselinePosition() const; |
| bool isBaselineAligned() const { |
| EVerticalAlign va = style()->verticalAlign(); |
| return va == EVerticalAlign::Baseline || va == EVerticalAlign::TextBottom || |
| va == EVerticalAlign::TextTop || va == EVerticalAlign::Super || |
| va == EVerticalAlign::Sub || va == EVerticalAlign::Length; |
| } |
| |
| // Align the cell in the block direction. This is done by calculating an |
| // intrinsic padding before and after the cell contents, so that all cells in |
| // the row get the same logical height. |
| void computeIntrinsicPadding(int rowHeight, |
| EVerticalAlign, |
| SubtreeLayoutScope&); |
| |
| void clearIntrinsicPadding() { setIntrinsicPadding(0, 0); } |
| |
| int intrinsicPaddingBefore() const { return m_intrinsicPaddingBefore; } |
| int intrinsicPaddingAfter() const { return m_intrinsicPaddingAfter; } |
| |
| LayoutUnit paddingTop() const override; |
| LayoutUnit paddingBottom() const override; |
| LayoutUnit paddingLeft() const override; |
| LayoutUnit paddingRight() const override; |
| |
| // FIXME: For now we just assume the cell has the same block flow direction as |
| // the table. It's likely we'll create an extra anonymous LayoutBlock to |
| // handle mixing directionality anyway, in which case we can lock the block |
| // flow directionality of the cells to the table's directionality. |
| LayoutUnit paddingBefore() const override; |
| LayoutUnit paddingAfter() const override; |
| |
| void setOverrideLogicalContentHeightFromRowHeight(LayoutUnit); |
| |
| void scrollbarsChanged(bool horizontalScrollbarChanged, |
| bool verticalScrollbarChanged, |
| ScrollbarChangeContext = Layout) override; |
| |
| bool cellWidthChanged() const { return m_cellWidthChanged; } |
| void setCellWidthChanged(bool b = true) { m_cellWidthChanged = b; } |
| |
| static LayoutTableCell* createAnonymous(Document*); |
| static LayoutTableCell* createAnonymousWithParent(const LayoutObject*); |
| LayoutBox* createAnonymousBoxWithSameTypeAs( |
| const LayoutObject* parent) const override { |
| return createAnonymousWithParent(parent); |
| } |
| |
| // This function is used to unify which table part's style we use for |
| // computing direction and writing mode. Writing modes are not allowed on row |
| // group and row but direction is. This means we can safely use the same style |
| // in all cases to simplify our code. |
| // FIXME: Eventually this function should replaced by style() once we support |
| // direction on all table parts and writing-mode on cells. |
| const ComputedStyle& styleForCellFlow() const { return row()->styleRef(); } |
| |
| const BorderValue& borderAdjoiningTableStart() const { |
| ASSERT(isFirstOrLastCellInRow()); |
| if (section()->hasSameDirectionAs(table())) |
| return style()->borderStart(); |
| |
| return style()->borderEnd(); |
| } |
| |
| const BorderValue& borderAdjoiningTableEnd() const { |
| ASSERT(isFirstOrLastCellInRow()); |
| if (section()->hasSameDirectionAs(table())) |
| return style()->borderEnd(); |
| |
| return style()->borderStart(); |
| } |
| |
| const BorderValue& borderAdjoiningCellBefore(const LayoutTableCell* cell) { |
| DCHECK_EQ(table()->cellAfter(cell), this); |
| // FIXME: https://webkit.org/b/79272 - Add support for mixed directionality |
| // at the cell level. |
| return style()->borderStart(); |
| } |
| |
| const BorderValue& borderAdjoiningCellAfter(const LayoutTableCell* cell) { |
| DCHECK_EQ(table()->cellBefore(cell), this); |
| // FIXME: https://webkit.org/b/79272 - Add support for mixed directionality |
| // at the cell level. |
| return style()->borderEnd(); |
| } |
| |
| #if DCHECK_IS_ON() |
| bool isFirstOrLastCellInRow() const { |
| return !table()->cellAfter(this) || !table()->cellBefore(this); |
| } |
| #endif |
| |
| const char* name() const override { return "LayoutTableCell"; } |
| |
| bool backgroundIsKnownToBeOpaqueInRect(const LayoutRect&) const override; |
| void invalidateDisplayItemClients(PaintInvalidationReason) const override; |
| |
| // TODO(wkorman): Consider renaming to more clearly differentiate from |
| // CollapsedBorderValue. |
| class CollapsedBorderValues : public DisplayItemClient { |
| public: |
| CollapsedBorderValues(const LayoutTableCell&, |
| const CollapsedBorderValue& startBorder, |
| const CollapsedBorderValue& endBorder, |
| const CollapsedBorderValue& beforeBorder, |
| const CollapsedBorderValue& afterBorder); |
| |
| const CollapsedBorderValue& startBorder() const { return m_startBorder; } |
| const CollapsedBorderValue& endBorder() const { return m_endBorder; } |
| const CollapsedBorderValue& beforeBorder() const { return m_beforeBorder; } |
| const CollapsedBorderValue& afterBorder() const { return m_afterBorder; } |
| |
| void setCollapsedBorderValues(const CollapsedBorderValues& other); |
| |
| // DisplayItemClient methods. |
| String debugName() const; |
| LayoutRect visualRect() const; |
| |
| private: |
| const LayoutTableCell& m_layoutTableCell; |
| CollapsedBorderValue m_startBorder; |
| CollapsedBorderValue m_endBorder; |
| CollapsedBorderValue m_beforeBorder; |
| CollapsedBorderValue m_afterBorder; |
| }; |
| |
| class RowBackgroundDisplayItemClient : public DisplayItemClient { |
| public: |
| RowBackgroundDisplayItemClient(const LayoutTableCell&); |
| |
| // DisplayItemClient methods. |
| String debugName() const; |
| LayoutRect visualRect() const; |
| |
| private: |
| const LayoutTableCell& m_layoutTableCell; |
| }; |
| |
| bool usesCompositedCellDisplayItemClients() const; |
| const CollapsedBorderValues* collapsedBorderValues() const { |
| return m_collapsedBorderValues.get(); |
| } |
| const DisplayItemClient& backgroundDisplayItemClient() const { |
| return m_rowBackgroundDisplayItemClient |
| ? static_cast<const DisplayItemClient&>( |
| *m_rowBackgroundDisplayItemClient) |
| : *this; |
| } |
| |
| LayoutRect debugRect() const override; |
| |
| void adjustChildDebugRect(LayoutRect&) const override; |
| |
| // A table cell's location is relative to its containing section. |
| LayoutBox* locationContainer() const override { return section(); } |
| |
| void ensureIsReadyForPaintInvalidation() override; |
| |
| bool hasLineIfEmpty() const override; |
| |
| protected: |
| void styleDidChange(StyleDifference, const ComputedStyle* oldStyle) override; |
| void computePreferredLogicalWidths() override; |
| |
| void addLayerHitTestRects(LayerHitTestRects&, |
| const PaintLayer* currentCompositedLayer, |
| const LayoutPoint& layerOffset, |
| const LayoutRect& containerRect) const override; |
| |
| private: |
| bool isOfType(LayoutObjectType type) const override { |
| return type == LayoutObjectTableCell || LayoutBlockFlow::isOfType(type); |
| } |
| |
| void willBeRemovedFromTree() override; |
| |
| void updateLogicalWidth() override; |
| |
| void paintBoxDecorationBackground(const PaintInfo&, |
| const LayoutPoint&) const override; |
| void paintMask(const PaintInfo&, const LayoutPoint&) const override; |
| |
| LayoutSize offsetFromContainer(const LayoutObject*) const override; |
| LayoutRect localVisualRect() const override; |
| |
| int borderHalfLeft(bool outer) const; |
| int borderHalfRight(bool outer) const; |
| int borderHalfTop(bool outer) const; |
| int borderHalfBottom(bool outer) const; |
| |
| int borderHalfStart(bool outer) const; |
| int borderHalfEnd(bool outer) const; |
| int borderHalfBefore(bool outer) const; |
| int borderHalfAfter(bool outer) const; |
| |
| void setIntrinsicPaddingBefore(int p) { m_intrinsicPaddingBefore = p; } |
| void setIntrinsicPaddingAfter(int p) { m_intrinsicPaddingAfter = p; } |
| void setIntrinsicPadding(int before, int after) { |
| setIntrinsicPaddingBefore(before); |
| setIntrinsicPaddingAfter(after); |
| } |
| |
| bool hasStartBorderAdjoiningTable() const; |
| bool hasEndBorderAdjoiningTable() const; |
| |
| // Those functions implement the CSS collapsing border conflict |
| // resolution algorithm. |
| // http://www.w3.org/TR/CSS2/tables.html#border-conflict-resolution |
| // |
| // The code is pretty complicated as it needs to handle mixed directionality |
| // between the table and the different table parts (cell, row, row group, |
| // column, column group). |
| // TODO(jchaffraix): It should be easier to compute all the borders in |
| // physical coordinates. However this is not the design of the current code. |
| // |
| // Blink's support for mixed directionality is currently partial. We only |
| // support the directionality up to |styleForCellFlow|. See comment on the |
| // function above for more details. |
| // See also https://code.google.com/p/chromium/issues/detail?id=128227 for |
| // some history. |
| // |
| // Those functions are called when the cache (m_collapsedBorders) is |
| // invalidated on LayoutTable. |
| CollapsedBorderValue computeCollapsedStartBorder( |
| IncludeBorderColorOrNot = IncludeBorderColor) const; |
| CollapsedBorderValue computeCollapsedEndBorder( |
| IncludeBorderColorOrNot = IncludeBorderColor) const; |
| CollapsedBorderValue computeCollapsedBeforeBorder( |
| IncludeBorderColorOrNot = IncludeBorderColor) const; |
| CollapsedBorderValue computeCollapsedAfterBorder( |
| IncludeBorderColorOrNot = IncludeBorderColor) const; |
| |
| Length logicalWidthFromColumns(LayoutTableCol* firstColForThisCell, |
| Length widthFromStyle) const; |
| |
| void updateColAndRowSpanFlags(); |
| |
| unsigned parseRowSpanFromDOM() const; |
| unsigned parseColSpanFromDOM() const; |
| |
| void nextSibling() const = delete; |
| void previousSibling() const = delete; |
| |
| // Note MSVC will only pack members if they have identical types, hence we use |
| // unsigned instead of bool here. |
| unsigned m_absoluteColumnIndex : 29; |
| unsigned m_cellWidthChanged : 1; |
| unsigned m_hasColSpan : 1; |
| unsigned m_hasRowSpan : 1; |
| |
| // The intrinsic padding. |
| // See class comment for what they are. |
| // |
| // Note: Those fields are using non-subpixel units (int) |
| // because we don't do fractional arithmetic on tables. |
| int m_intrinsicPaddingBefore; |
| int m_intrinsicPaddingAfter; |
| |
| std::unique_ptr<CollapsedBorderValues> m_collapsedBorderValues; |
| std::unique_ptr<RowBackgroundDisplayItemClient> |
| m_rowBackgroundDisplayItemClient; |
| }; |
| |
| DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutTableCell, isTableCell()); |
| |
| inline LayoutTableCell* LayoutTableCell::previousCell() const { |
| return toLayoutTableCell(LayoutObject::previousSibling()); |
| } |
| |
| inline LayoutTableCell* LayoutTableCell::nextCell() const { |
| return toLayoutTableCell(LayoutObject::nextSibling()); |
| } |
| |
| inline LayoutTableCell* LayoutTableRow::firstCell() const { |
| return toLayoutTableCell(firstChild()); |
| } |
| |
| inline LayoutTableCell* LayoutTableRow::lastCell() const { |
| return toLayoutTableCell(lastChild()); |
| } |
| |
| } // namespace blink |
| |
| #endif // LayoutTableCell_h |