blob: 87894828d679bff724a9b0adefe525186176be38 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/layout/ng/ng_length_utils.h"
#include "core/layout/ng/ng_constraint_space.h"
#include "core/style/ComputedStyle.h"
#include "platform/LayoutUnit.h"
#include "platform/Length.h"
namespace blink {
// TODO(layout-ng):
// - positioned and/or replaced calculations
// - Take scrollbars into account
namespace {
// Converts physical dimensions to logical ones per
// https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical
// For now it's only used to calculate abstract values for margins.
NGBoxStrut ToLogicalDimensions(const NGPhysicalDimensions& physical_dim,
const NGWritingMode writing_mode,
const NGDirection direction) {
bool is_ltr = direction == LeftToRight;
NGBoxStrut logical_dim;
switch (writing_mode) {
case VerticalRightLeft:
case SidewaysRightLeft:
logical_dim = {is_ltr ? physical_dim.top : physical_dim.bottom,
is_ltr ? physical_dim.bottom : physical_dim.top,
physical_dim.right, physical_dim.left};
break;
case VerticalLeftRight:
logical_dim = {is_ltr ? physical_dim.top : physical_dim.bottom,
is_ltr ? physical_dim.bottom : physical_dim.top,
physical_dim.left, physical_dim.right};
break;
case SidewaysLeftRight:
logical_dim = {is_ltr ? physical_dim.bottom : physical_dim.top,
is_ltr ? physical_dim.top : physical_dim.bottom,
physical_dim.left, physical_dim.right};
break;
default:
NOTREACHED();
/* FALLTHROUGH */
case HorizontalTopBottom:
logical_dim = {is_ltr ? physical_dim.left : physical_dim.right,
is_ltr ? physical_dim.right : physical_dim.left,
physical_dim.top, physical_dim.bottom};
break;
}
return logical_dim;
}
} // namespace
LayoutUnit resolveInlineLength(const NGConstraintSpace& constraintSpace,
const ComputedStyle& style,
const Length& length,
LengthResolveType type) {
// TODO(layout-ng): Handle min/max/fit-content
DCHECK(!length.isMaxSizeNone());
DCHECK_GE(constraintSpace.ContainerSize().inline_size, LayoutUnit());
if (type == LengthResolveType::MinSize && length.isAuto())
return LayoutUnit();
if (type == LengthResolveType::MarginBorderPaddingSize && length.isAuto())
return LayoutUnit();
// We don't need this when we're resolving margin/border/padding; skip
// computing it as an optimization and to simplify the code below.
NGBoxStrut border_and_padding;
if (type != LengthResolveType::MarginBorderPaddingSize) {
border_and_padding =
computeBorders(style) + computePadding(constraintSpace, style);
}
LayoutUnit container_size = constraintSpace.ContainerSize().inline_size;
switch (length.type()) {
case Auto:
case FillAvailable: {
NGBoxStrut margins =
computeMargins(constraintSpace, style,
FromPlatformWritingMode(style.getWritingMode()),
FromPlatformDirection(style.direction()));
return std::max(border_and_padding.InlineSum(),
container_size - margins.InlineSum());
}
case Percent:
case Fixed:
case Calculated: {
LayoutUnit value = valueForLength(length, container_size);
if (style.boxSizing() == BoxSizingContentBox) {
value += border_and_padding.InlineSum();
} else {
value = std::max(border_and_padding.InlineSum(), value);
}
return value;
}
case MinContent:
case MaxContent:
case FitContent:
// TODO(layout-ng): implement
return border_and_padding.InlineSum();
case DeviceWidth:
case DeviceHeight:
case ExtendToZoom:
NOTREACHED() << "These should only be used for viewport definitions";
case MaxSizeNone:
default:
NOTREACHED();
return border_and_padding.InlineSum();
}
}
LayoutUnit resolveBlockLength(const NGConstraintSpace& constraintSpace,
const ComputedStyle& style,
const Length& length,
LayoutUnit contentSize,
LengthResolveType type) {
DCHECK(!length.isMaxSizeNone());
DCHECK(type != LengthResolveType::MarginBorderPaddingSize);
if (type == LengthResolveType::MinSize && length.isAuto())
return LayoutUnit();
// Make sure that indefinite percentages resolve to NGSizeIndefinite, not to
// a random negative number.
if (length.isPercentOrCalc() &&
constraintSpace.ContainerSize().block_size == NGSizeIndefinite)
return contentSize;
// We don't need this when we're resolving margin/border/padding; skip
// computing it as an optimization and to simplify the code below.
NGBoxStrut border_and_padding;
if (type != LengthResolveType::MarginBorderPaddingSize) {
border_and_padding =
computeBorders(style) + computePadding(constraintSpace, style);
}
LayoutUnit container_size = constraintSpace.ContainerSize().block_size;
switch (length.type()) {
case FillAvailable: {
NGBoxStrut margins =
computeMargins(constraintSpace, style,
FromPlatformWritingMode(style.getWritingMode()),
FromPlatformDirection(style.direction()));
return std::max(border_and_padding.BlockSum(),
container_size - margins.BlockSum());
}
case Percent:
case Fixed:
case Calculated: {
LayoutUnit value = valueForLength(length, container_size);
if (style.boxSizing() == BoxSizingContentBox) {
value += border_and_padding.BlockSum();
} else {
value = std::max(border_and_padding.BlockSum(), value);
}
return value;
}
case Auto:
case MinContent:
case MaxContent:
case FitContent:
// Due to how contentSize is calculated, it should always include border
// and padding.
if (contentSize != LayoutUnit(-1))
DCHECK_GE(contentSize, border_and_padding.BlockSum());
return contentSize;
case DeviceWidth:
case DeviceHeight:
case ExtendToZoom:
NOTREACHED() << "These should only be used for viewport definitions";
case MaxSizeNone:
default:
NOTREACHED();
return border_and_padding.BlockSum();
}
}
LayoutUnit computeInlineSizeForFragment(
const NGConstraintSpace& constraintSpace,
const ComputedStyle& style) {
if (constraintSpace.FixedInlineSize())
return constraintSpace.ContainerSize().inline_size;
LayoutUnit extent =
resolveInlineLength(constraintSpace, style, style.logicalWidth(),
LengthResolveType::ContentSize);
Length maxLength = style.logicalMaxWidth();
if (!maxLength.isMaxSizeNone()) {
LayoutUnit max = resolveInlineLength(constraintSpace, style, maxLength,
LengthResolveType::MaxSize);
extent = std::min(extent, max);
}
LayoutUnit min =
resolveInlineLength(constraintSpace, style, style.logicalMinWidth(),
LengthResolveType::MinSize);
extent = std::max(extent, min);
return extent;
}
LayoutUnit computeBlockSizeForFragment(const NGConstraintSpace& constraintSpace,
const ComputedStyle& style,
LayoutUnit contentSize) {
if (constraintSpace.FixedBlockSize())
return constraintSpace.ContainerSize().block_size;
LayoutUnit extent =
resolveBlockLength(constraintSpace, style, style.logicalHeight(),
contentSize, LengthResolveType::ContentSize);
if (extent == NGSizeIndefinite) {
DCHECK_EQ(contentSize, NGSizeIndefinite);
return extent;
}
Length maxLength = style.logicalMaxHeight();
if (!maxLength.isMaxSizeNone()) {
LayoutUnit max =
resolveBlockLength(constraintSpace, style, maxLength, contentSize,
LengthResolveType::MaxSize);
extent = std::min(extent, max);
}
LayoutUnit min =
resolveBlockLength(constraintSpace, style, style.logicalMinHeight(),
contentSize, LengthResolveType::MinSize);
extent = std::max(extent, min);
return extent;
}
NGBoxStrut computeMargins(const NGConstraintSpace& constraintSpace,
const ComputedStyle& style,
const NGWritingMode writing_mode,
const NGDirection direction) {
// Margins always get computed relative to the inline size:
// https://www.w3.org/TR/CSS2/box.html#value-def-margin-width
NGPhysicalDimensions physical_dim;
physical_dim.left =
resolveInlineLength(constraintSpace, style, style.marginLeft(),
LengthResolveType::MarginBorderPaddingSize);
physical_dim.right =
resolveInlineLength(constraintSpace, style, style.marginRight(),
LengthResolveType::MarginBorderPaddingSize);
physical_dim.top =
resolveInlineLength(constraintSpace, style, style.marginTop(),
LengthResolveType::MarginBorderPaddingSize);
physical_dim.bottom =
resolveInlineLength(constraintSpace, style, style.marginBottom(),
LengthResolveType::MarginBorderPaddingSize);
return ToLogicalDimensions(physical_dim, writing_mode, direction);
}
NGBoxStrut computeBorders(const ComputedStyle& style) {
NGBoxStrut borders;
borders.inline_start = LayoutUnit(style.borderStartWidth());
borders.inline_end = LayoutUnit(style.borderEndWidth());
borders.block_start = LayoutUnit(style.borderBeforeWidth());
borders.block_end = LayoutUnit(style.borderAfterWidth());
return borders;
}
NGBoxStrut computePadding(const NGConstraintSpace& constraintSpace,
const ComputedStyle& style) {
// Padding always gets computed relative to the inline size:
// https://www.w3.org/TR/CSS2/box.html#value-def-padding-width
NGBoxStrut padding;
padding.inline_start =
resolveInlineLength(constraintSpace, style, style.paddingStart(),
LengthResolveType::MarginBorderPaddingSize);
padding.inline_end =
resolveInlineLength(constraintSpace, style, style.paddingEnd(),
LengthResolveType::MarginBorderPaddingSize);
padding.block_start =
resolveInlineLength(constraintSpace, style, style.paddingBefore(),
LengthResolveType::MarginBorderPaddingSize);
padding.block_end =
resolveInlineLength(constraintSpace, style, style.paddingAfter(),
LengthResolveType::MarginBorderPaddingSize);
return padding;
}
} // namespace blink