blob: f0489178efe2b2bc74056d493d876ab7488fb4c5 [file] [log] [blame]
/*
* Copyright (C) 2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "TableFormattingGeometry.h"
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "InlineFormattingState.h"
#include "LayoutBoxGeometry.h"
#include "LayoutContext.h"
#include "LayoutDescendantIterator.h"
#include "LayoutInitialContainingBlock.h"
#include "TableFormattingContext.h"
#include "TableFormattingQuirks.h"
namespace WebCore {
namespace Layout {
TableFormattingGeometry::TableFormattingGeometry(const TableFormattingContext& tableFormattingContext)
: FormattingGeometry(tableFormattingContext)
{
}
LayoutUnit TableFormattingGeometry::cellBoxContentHeight(const ContainerBox& cellBox) const
{
ASSERT(cellBox.isInFlow());
if (layoutState().inQuirksMode() && TableFormattingQuirks::shouldIgnoreChildContentVerticalMargin(cellBox)) {
ASSERT(cellBox.firstInFlowChild());
auto formattingContext = this->formattingContext();
auto& firstInFlowChild = *cellBox.firstInFlowChild();
auto& lastInFlowChild = *cellBox.lastInFlowChild();
auto& firstInFlowChildGeometry = formattingContext.geometryForBox(firstInFlowChild, FormattingContext::EscapeReason::TableQuirkNeedsGeometryFromEstablishedFormattingContext);
auto& lastInFlowChildGeometry = formattingContext.geometryForBox(lastInFlowChild, FormattingContext::EscapeReason::TableQuirkNeedsGeometryFromEstablishedFormattingContext);
auto top = firstInFlowChild.style().hasMarginBeforeQuirk() ? BoxGeometry::borderBoxRect(firstInFlowChildGeometry).top() : BoxGeometry::marginBoxRect(firstInFlowChildGeometry).top();
auto bottom = lastInFlowChild.style().hasMarginAfterQuirk() ? BoxGeometry::borderBoxRect(lastInFlowChildGeometry).bottom() : BoxGeometry::marginBoxRect(lastInFlowChildGeometry).bottom();
return bottom - top;
}
return contentHeightForFormattingContextRoot(cellBox);
}
Edges TableFormattingGeometry::computedCellBorder(const TableGrid::Cell& cell) const
{
auto& grid = formattingContext().formattingState().tableGrid();
auto& cellBox = cell.box();
auto border = computedBorder(cellBox);
auto collapsedBorder = grid.collapsedBorder();
if (!collapsedBorder)
return border;
// We might want to cache these collapsed borders on the grid.
auto cellPosition = cell.position();
// Collapsed border left from table and adjacent cells.
if (!cellPosition.column)
border.horizontal.left = collapsedBorder->horizontal.left / 2;
else {
auto adjacentBorderRight = computedBorder(grid.slot({ cellPosition.column - 1, cellPosition.row })->cell().box()).horizontal.right;
border.horizontal.left = std::max(border.horizontal.left, adjacentBorderRight) / 2;
}
// Collapsed border right from table and adjacent cells.
if (cellPosition.column == grid.columns().size() - 1)
border.horizontal.right = collapsedBorder->horizontal.right / 2;
else {
auto adjacentBorderLeft = computedBorder(grid.slot({ cellPosition.column + 1, cellPosition.row })->cell().box()).horizontal.left;
border.horizontal.right = std::max(border.horizontal.right, adjacentBorderLeft) / 2;
}
// Collapsed border top from table, row and adjacent cells.
auto& rows = grid.rows().list();
if (!cellPosition.row)
border.vertical.top = collapsedBorder->vertical.top / 2;
else {
auto adjacentBorderBottom = computedBorder(grid.slot({ cellPosition.column, cellPosition.row - 1 })->cell().box()).vertical.bottom;
auto adjacentRowBottom = computedBorder(rows[cellPosition.row - 1].box()).vertical.bottom;
auto adjacentCollapsedBorder = std::max(adjacentBorderBottom, adjacentRowBottom);
border.vertical.top = std::max(border.vertical.top, adjacentCollapsedBorder) / 2;
}
// Collapsed border bottom from table, row and adjacent cells.
if (cellPosition.row == grid.rows().size() - 1)
border.vertical.bottom = collapsedBorder->vertical.bottom / 2;
else {
auto adjacentBorderTop = computedBorder(grid.slot({ cellPosition.column, cellPosition.row + 1 })->cell().box()).vertical.top;
auto adjacentRowTop = computedBorder(rows[cellPosition.row + 1].box()).vertical.top;
auto adjacentCollapsedBorder = std::max(adjacentBorderTop, adjacentRowTop);
border.vertical.bottom = std::max(border.vertical.bottom, adjacentCollapsedBorder) / 2;
}
return border;
}
std::optional<LayoutUnit> TableFormattingGeometry::computedColumnWidth(const ContainerBox& columnBox) const
{
// Check both style and <col>'s width attribute.
// FIXME: Figure out what to do with calculated values, like <col style="width: 10%">.
if (auto computedWidthValue = computedWidth(columnBox, { }))
return computedWidthValue;
return columnBox.columnWidth();
}
IntrinsicWidthConstraints TableFormattingGeometry::intrinsicWidthConstraintsForCellContent(const TableGrid::Cell& cell) const
{
auto& cellBox = cell.box();
if (!cellBox.hasInFlowOrFloatingChild())
return { };
auto& layoutState = this->layoutState();
return LayoutContext::createFormattingContext(cellBox, const_cast<LayoutState&>(layoutState))->computedIntrinsicWidthConstraints();
}
InlineLayoutUnit TableFormattingGeometry::usedBaselineForCell(const ContainerBox& cellBox) const
{
// The baseline of a cell is defined as the baseline of the first in-flow line box in the cell,
// or the first in-flow table-row in the cell, whichever comes first.
// If there is no such line box, the baseline is the bottom of content edge of the cell box.
if (cellBox.establishesInlineFormattingContext())
return layoutState().formattingStateForInlineFormattingContext(cellBox).lines()[0].baseline();
for (auto& cellDescendant : descendantsOfType<ContainerBox>(cellBox)) {
if (cellDescendant.establishesInlineFormattingContext()) {
auto& inlineFormattingStateForCell = layoutState().formattingStateForInlineFormattingContext(cellDescendant);
if (!inlineFormattingStateForCell.lines().isEmpty())
return inlineFormattingStateForCell.lines()[0].baseline();
}
if (cellDescendant.establishesTableFormattingContext())
return layoutState().formattingStateForTableFormattingContext(cellDescendant).tableGrid().rows().list()[0].baseline();
}
return formattingContext().geometryForBox(cellBox).contentBoxBottom();
}
LayoutUnit TableFormattingGeometry::horizontalSpaceForCellContent(const TableGrid::Cell& cell) const
{
auto& grid = formattingContext().formattingState().tableGrid();
auto& columnList = grid.columns().list();
auto logicalWidth = LayoutUnit { };
for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex)
logicalWidth += columnList.at(columnIndex).usedLogicalWidth();
// No column spacing when spanning.
logicalWidth += (cell.columnSpan() - 1) * grid.horizontalSpacing();
auto& cellBoxGeometry = formattingContext().geometryForBox(cell.box());
logicalWidth -= (cellBoxGeometry.horizontalBorder() + cellBoxGeometry.horizontalPadding().value_or(0));
return logicalWidth;
}
LayoutUnit TableFormattingGeometry::verticalSpaceForCellContent(const TableGrid::Cell& cell, std::optional<LayoutUnit> availableVerticalSpace) const
{
auto& cellBox = cell.box();
auto contentHeight = cellBoxContentHeight(cellBox);
auto computedHeight = this->computedHeight(cellBox, availableVerticalSpace);
if (!computedHeight)
return contentHeight;
auto heightUsesBorderBox = layoutState().inQuirksMode() || cellBox.style().boxSizing() == BoxSizing::BorderBox;
if (heightUsesBorderBox) {
auto& cellBoxGeometry = formattingContext().geometryForBox(cell.box());
*computedHeight -= (cellBoxGeometry.verticalBorder() + cellBoxGeometry.verticalPadding().value_or(0));
}
return std::max(contentHeight, *computedHeight);
}
}
}
#endif