summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc')
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc858
1 files changed, 597 insertions, 261 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
index 9bbdcdbedba..21c01f8a5be 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
@@ -9,12 +9,16 @@
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_flex_child_iterator.h"
#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
@@ -27,10 +31,18 @@ NGFlexLayoutAlgorithm::NGFlexLayoutAlgorithm(
border_scrollbar_padding_(border_padding_ +
params.fragment_geometry.scrollbar),
is_column_(Style().ResolvedIsColumnFlexDirection()),
- is_horizontal_flow_(FlexLayoutAlgorithm::IsHorizontalFlow(Style())) {
+ is_horizontal_flow_(FlexLayoutAlgorithm::IsHorizontalFlow(Style())),
+ is_cross_size_definite_(IsContainerCrossSizeDefinite()) {
container_builder_.SetIsNewFormattingContext(
params.space.IsNewFormattingContext());
container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
+
+ border_box_size_ = container_builder_.InitialBorderBoxSize();
+ content_box_size_ =
+ ShrinkAvailableSize(border_box_size_, border_scrollbar_padding_);
+ child_percentage_size_ = CalculateChildPercentageSize(
+ ConstraintSpace(), Node(), content_box_size_);
+ algorithm_.emplace(&Style(), MainAxisContentExtent(LayoutUnit::Max()));
}
bool NGFlexLayoutAlgorithm::MainAxisIsInlineAxis(
@@ -40,26 +52,101 @@ bool NGFlexLayoutAlgorithm::MainAxisIsInlineAxis(
}
LayoutUnit NGFlexLayoutAlgorithm::MainAxisContentExtent(
- LayoutUnit sum_hypothetical_main_size) {
+ LayoutUnit sum_hypothetical_main_size) const {
if (Style().ResolvedIsColumnFlexDirection()) {
return ComputeBlockSizeForFragment(
ConstraintSpace(), Style(), border_padding_,
- sum_hypothetical_main_size + (border_padding_).BlockSum()) -
+ sum_hypothetical_main_size +
+ border_scrollbar_padding_.BlockSum()) -
border_scrollbar_padding_.BlockSum();
}
return content_box_size_.inline_size;
}
+namespace {
+
+enum AxisEdge { kStart, kCenter, kEnd };
+
+// Maps the resolved justify-content value to a static-position edge.
+AxisEdge MainAxisStaticPositionEdge(const ComputedStyle& style,
+ bool is_column) {
+ const StyleContentAlignmentData justify =
+ FlexLayoutAlgorithm::ResolvedJustifyContent(style);
+ const ContentPosition content_position = justify.GetPosition();
+ bool is_reverse_flex = is_column
+ ? style.ResolvedIsColumnReverseFlexDirection()
+ : style.ResolvedIsRowReverseFlexDirection();
+
+ if (content_position == ContentPosition::kFlexEnd)
+ return is_reverse_flex ? AxisEdge::kStart : AxisEdge::kEnd;
+
+ if (content_position == ContentPosition::kCenter ||
+ justify.Distribution() == ContentDistributionType::kSpaceAround ||
+ justify.Distribution() == ContentDistributionType::kSpaceEvenly)
+ return AxisEdge::kCenter;
+
+ return is_reverse_flex ? AxisEdge::kEnd : AxisEdge::kStart;
+}
+
+// Maps the resolved alignment value to a static-position edge.
+AxisEdge CrossAxisStaticPositionEdge(const ComputedStyle& style,
+ const ComputedStyle& child_style) {
+ ItemPosition alignment =
+ FlexLayoutAlgorithm::AlignmentForChild(style, child_style);
+ bool is_wrap_reverse = style.FlexWrap() == EFlexWrap::kWrapReverse;
+
+ if (alignment == ItemPosition::kFlexEnd)
+ return is_wrap_reverse ? AxisEdge::kStart : AxisEdge::kEnd;
+
+ if (alignment == ItemPosition::kCenter)
+ return AxisEdge::kCenter;
+
+ return is_wrap_reverse ? AxisEdge::kEnd : AxisEdge::kStart;
+}
+
+} // namespace
+
void NGFlexLayoutAlgorithm::HandleOutOfFlowPositioned(NGBlockNode child) {
- // TODO(dgrogan): There's stuff from
- // https://www.w3.org/TR/css-flexbox-1/#abspos-items that isn't done here.
- // Specifically, neither rtl nor alignment is handled here, at least.
- // Look at LayoutFlexibleBox::PrepareChildForPositionedLayout and
- // SetStaticPositionForPositionedLayout to see how to statically position
- // this.
- container_builder_.AddOutOfFlowChildCandidate(
- child, {border_scrollbar_padding_.inline_start,
- border_scrollbar_padding_.block_start});
+ AxisEdge main_axis_edge = MainAxisStaticPositionEdge(Style(), is_column_);
+ AxisEdge cross_axis_edge =
+ CrossAxisStaticPositionEdge(Style(), child.Style());
+
+ AxisEdge inline_axis_edge = is_column_ ? cross_axis_edge : main_axis_edge;
+ AxisEdge block_axis_edge = is_column_ ? main_axis_edge : cross_axis_edge;
+
+ using InlineEdge = NGLogicalStaticPosition::InlineEdge;
+ using BlockEdge = NGLogicalStaticPosition::BlockEdge;
+
+ InlineEdge inline_edge;
+ BlockEdge block_edge;
+ LogicalOffset offset(border_scrollbar_padding_.inline_start,
+ border_scrollbar_padding_.block_start);
+
+ // Determine the static-position based off the axis-edge.
+ if (inline_axis_edge == AxisEdge::kStart) {
+ inline_edge = InlineEdge::kInlineStart;
+ } else if (inline_axis_edge == AxisEdge::kCenter) {
+ inline_edge = InlineEdge::kInlineCenter;
+ offset.inline_offset += content_box_size_.inline_size / 2;
+ } else {
+ inline_edge = InlineEdge::kInlineEnd;
+ offset.inline_offset += content_box_size_.inline_size;
+ }
+
+ // We may not know the final block-size of the fragment yet. This will be
+ // adjusted within the |NGContainerFragmentBuilder| once set.
+ if (block_axis_edge == AxisEdge::kStart) {
+ block_edge = BlockEdge::kBlockStart;
+ } else if (block_axis_edge == AxisEdge::kCenter) {
+ block_edge = BlockEdge::kBlockCenter;
+ offset.block_offset -= border_scrollbar_padding_.BlockSum() / 2;
+ } else {
+ block_edge = BlockEdge::kBlockEnd;
+ offset.block_offset -= border_scrollbar_padding_.BlockSum();
+ }
+
+ container_builder_.AddOutOfFlowChildCandidate(child, offset, inline_edge,
+ block_edge);
}
bool NGFlexLayoutAlgorithm::IsColumnContainerMainSizeDefinite() const {
@@ -110,13 +197,27 @@ bool NGFlexLayoutAlgorithm::DoesItemStretch(const NGBlockNode& child) const {
ItemPosition::kStretch;
}
+bool NGFlexLayoutAlgorithm::IsItemFlexBasisDefinite(
+ const NGBlockNode& child) const {
+ const Length& flex_basis = child.Style().FlexBasis();
+ DCHECK(!flex_basis.IsAuto())
+ << "This is never called with flex_basis.IsAuto, but it'd be trivial to "
+ "support.";
+ if (!is_column_)
+ return true;
+ return !BlockLengthUnresolvable(BuildSpaceForFlexBasis(child), flex_basis,
+ LengthResolvePhase::kLayout);
+}
+
// This behavior is under discussion: the item's pre-flexing main size
// definiteness may no longer imply post-flexing definiteness.
// TODO(dgrogan): Have https://crbug.com/1003506 and
// https://github.com/w3c/csswg-drafts/issues/4305 been resolved yet?
bool NGFlexLayoutAlgorithm::IsItemMainSizeDefinite(
const NGBlockNode& child) const {
- DCHECK(is_column_);
+ DCHECK(is_column_)
+ << "This method doesn't work with row flexboxes because we assume "
+ "main size is block size when we call BlockLengthUnresolvable.";
// Inline sizes are always definite.
// TODO(dgrogan): The relevant tests, the last two cases in
// css/css-flexbox/percentage-heights-003.html passed even without this, so it
@@ -126,9 +227,8 @@ bool NGFlexLayoutAlgorithm::IsItemMainSizeDefinite(
// We need a constraint space for the child to determine resolvability and the
// space for flex-basis is sufficient, even though it has some unnecessary
// stuff (ShrinkToFit and fixed cross sizes).
- NGConstraintSpace child_space =
- BuildConstraintSpaceForDeterminingFlexBasis(child);
- return !BlockLengthUnresolvable(child_space, child.Style().LogicalHeight(),
+ return !BlockLengthUnresolvable(BuildSpaceForFlexBasis(child),
+ child.Style().LogicalHeight(),
LengthResolvePhase::kLayout);
}
@@ -143,9 +243,8 @@ bool NGFlexLayoutAlgorithm::IsItemCrossAxisLengthDefinite(
if (!MainAxisIsInlineAxis(child))
return true;
// If we get here, cross axis is block axis.
- return !BlockLengthUnresolvable(
- BuildConstraintSpaceForDeterminingFlexBasis(child), length,
- LengthResolvePhase::kLayout);
+ return !BlockLengthUnresolvable(BuildSpaceForFlexBasis(child), length,
+ LengthResolvePhase::kLayout);
}
bool NGFlexLayoutAlgorithm::DoesItemCrossSizeComputeToAuto(
@@ -197,139 +296,157 @@ bool NGFlexLayoutAlgorithm::ShouldItemShrinkToFit(
bool NGFlexLayoutAlgorithm::WillChildCrossSizeBeContainerCrossSize(
const NGBlockNode& child) const {
- return !algorithm_->IsMultiline() && IsContainerCrossSizeDefinite() &&
+ return !algorithm_->IsMultiline() && is_cross_size_definite_ &&
DoesItemStretch(child);
}
-NGConstraintSpace
-NGFlexLayoutAlgorithm::BuildConstraintSpaceForDeterminingFlexBasis(
- const NGBlockNode& flex_item) const {
+double NGFlexLayoutAlgorithm::GetMainOverCrossAspectRatio(
+ const NGBlockNode& child) const {
+ DCHECK(child.HasAspectRatio());
+ LogicalSize aspect_ratio = child.GetAspectRatio();
+
+ DCHECK_GT(aspect_ratio.inline_size, 0);
+ DCHECK_GT(aspect_ratio.block_size, 0);
+
+ double ratio =
+ aspect_ratio.inline_size.ToDouble() / aspect_ratio.block_size.ToDouble();
+ // Multiplying by ratio will take something in the item's block axis and
+ // convert it to the inline axis. We want to convert from cross size to main
+ // size. If block axis and cross axis are the same, then we already have what
+ // we need. Otherwise we need to use the reciprocal.
+ if (!MainAxisIsInlineAxis(child))
+ ratio = 1 / ratio;
+ return ratio;
+}
+
+namespace {
+
+LayoutUnit CalculateFixedCrossSize(LayoutUnit available_size,
+ const MinMaxSizes& cross_axis_min_max,
+ LayoutUnit margin_sum) {
+ return cross_axis_min_max.ClampSizeToMinAndMax(available_size - margin_sum);
+}
+
+} // namespace
+
+NGConstraintSpace NGFlexLayoutAlgorithm::BuildSpaceForIntrinsicBlockSize(
+ const NGBlockNode& flex_item,
+ const NGPhysicalBoxStrut& physical_margins,
+ const MinMaxSizes& cross_axis_min_max) const {
const ComputedStyle& child_style = flex_item.Style();
NGConstraintSpaceBuilder space_builder(ConstraintSpace(),
child_style.GetWritingMode(),
/* is_new_fc */ true);
SetOrthogonalFallbackInlineSizeIfNeeded(Style(), flex_item, &space_builder);
+ space_builder.SetCacheSlot(NGCacheSlot::kMeasure);
+ space_builder.SetIsPaintedAtomically(true);
- if (ShouldItemShrinkToFit(flex_item))
+ NGBoxStrut margins = physical_margins.ConvertToLogical(
+ ConstraintSpace().GetWritingMode(), Style().Direction());
+ LogicalSize child_available_size = content_box_size_;
+ if (ShouldItemShrinkToFit(flex_item)) {
space_builder.SetIsShrinkToFit(true);
- if (WillChildCrossSizeBeContainerCrossSize(flex_item)) {
+ } else if (WillChildCrossSizeBeContainerCrossSize(flex_item)) {
if (is_column_) {
space_builder.SetIsFixedInlineSize(true);
+ child_available_size.inline_size =
+ CalculateFixedCrossSize(child_available_size.inline_size,
+ cross_axis_min_max, margins.InlineSum());
} else {
space_builder.SetIsFixedBlockSize(true);
DCHECK_NE(content_box_size_.block_size, kIndefiniteSize);
+ child_available_size.block_size =
+ CalculateFixedCrossSize(child_available_size.block_size,
+ cross_axis_min_max, margins.BlockSum());
}
}
+ space_builder.SetNeedsBaseline(
+ ConstraintSpace().NeedsBaseline() ||
+ FlexLayoutAlgorithm::AlignmentForChild(Style(), child_style) ==
+ ItemPosition::kBaseline);
+
+ // For determining the intrinsic block-size we make %-block-sizes resolve
+ // against an indefinite size.
+ LogicalSize child_percentage_size = child_percentage_size_;
+ if (is_column_)
+ child_percentage_size.block_size = kIndefiniteSize;
+
+ space_builder.SetAvailableSize(child_available_size);
+ space_builder.SetPercentageResolutionSize(child_percentage_size);
+ // TODO(dgrogan): The SetReplacedPercentageResolutionSize calls in this file
+ // may be untested. Write a test or determine why they're unnecessary.
+ space_builder.SetReplacedPercentageResolutionSize(child_percentage_size);
+ space_builder.SetTextDirection(child_style.Direction());
+ return space_builder.ToConstraintSpace();
+}
+
+NGConstraintSpace NGFlexLayoutAlgorithm::BuildSpaceForFlexBasis(
+ const NGBlockNode& flex_item) const {
+ NGConstraintSpaceBuilder space_builder(ConstraintSpace(),
+ flex_item.Style().GetWritingMode(),
+ /* is_new_fc */ true);
+ SetOrthogonalFallbackInlineSizeIfNeeded(Style(), flex_item, &space_builder);
+
+ // This space is only used for resolving lengths, not for layout. We only
+ // need the available and percentage sizes.
space_builder.SetAvailableSize(content_box_size_);
space_builder.SetPercentageResolutionSize(child_percentage_size_);
- space_builder.SetTextDirection(child_style.Direction());
+ space_builder.SetReplacedPercentageResolutionSize(child_percentage_size_);
return space_builder.ToConstraintSpace();
}
void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
- for (NGLayoutInputNode generic_child = Node().FirstChild(); generic_child;
- generic_child = generic_child.NextSibling()) {
- auto child = To<NGBlockNode>(generic_child);
+ NGFlexChildIterator iterator(Node());
+ for (NGBlockNode child = iterator.NextChild(); child;
+ child = iterator.NextChild()) {
if (child.IsOutOfFlowPositioned()) {
HandleOutOfFlowPositioned(child);
continue;
}
const ComputedStyle& child_style = child.Style();
- NGConstraintSpace child_space =
- BuildConstraintSpaceForDeterminingFlexBasis(child);
+ NGConstraintSpace flex_basis_space = BuildSpaceForFlexBasis(child);
+
+ NGPhysicalBoxStrut physical_child_margins =
+ ComputePhysicalMargins(flex_basis_space, child_style);
NGBoxStrut border_padding_in_child_writing_mode =
- ComputeBorders(child_space, child) +
- ComputePadding(child_space, child_style);
- NGBoxStrut border_scrollbar_padding_in_child_writing_mode =
- border_padding_in_child_writing_mode +
- ComputeScrollbars(child_space, child);
+ ComputeBorders(flex_basis_space, child_style) +
+ ComputePadding(flex_basis_space, child_style);
NGPhysicalBoxStrut physical_border_padding(
border_padding_in_child_writing_mode.ConvertToPhysical(
child_style.GetWritingMode(), child_style.Direction()));
- NGPhysicalBoxStrut physical_border_scrollbar_padding(
- border_scrollbar_padding_in_child_writing_mode.ConvertToPhysical(
- child_style.GetWritingMode(), child_style.Direction()));
LayoutUnit main_axis_border_padding =
is_horizontal_flow_ ? physical_border_padding.HorizontalSum()
: physical_border_padding.VerticalSum();
- LayoutUnit main_axis_border_scrollbar_padding =
- is_horizontal_flow_ ? physical_border_scrollbar_padding.HorizontalSum()
- : physical_border_scrollbar_padding.VerticalSum();
-
- // We want the child's min/max size in its writing mode, not ours. We'll
- // only ever use it if the child's inline axis is our main axis.
- MinMaxSizeInput input(
- /* percentage_resolution_block_size */ content_box_size_.block_size);
- MinMaxSize intrinsic_sizes_border_box = child.ComputeMinMaxSize(
- child_style.GetWritingMode(), input, &child_space);
- // TODO(dgrogan): Don't layout every time, just when you need to.
- // Use ChildHasIntrinsicMainAxisSize as a guide.
- scoped_refptr<const NGLayoutResult> layout_result =
- child.Layout(child_space, nullptr /*break token*/);
- NGFragment fragment_in_child_writing_mode(
- child_style.GetWritingMode(), layout_result->PhysicalFragment());
-
- LayoutUnit flex_base_border_box;
- const Length& specified_length_in_main_axis =
- is_horizontal_flow_ ? child_style.Width() : child_style.Height();
- const Length& flex_basis = child_style.FlexBasis();
- // TODO(dgrogan): Generalize IsAuto: See the <'width'> section of
- // https://drafts.csswg.org/css-flexbox/#valdef-flex-flex-basis
- // and https://drafts.csswg.org/css-flexbox/#flex-basis-property, which says
- // that if a flex-basis value would resolve to auto (but not literally auto)
- // we should interpret it as flex-basis:content.
- if (flex_basis.IsAuto() && specified_length_in_main_axis.IsAuto()) {
- if (MainAxisIsInlineAxis(child))
- flex_base_border_box = intrinsic_sizes_border_box.max_size;
- else
- flex_base_border_box = fragment_in_child_writing_mode.BlockSize();
- } else {
- // TODO(dgrogan): Check for definiteness.
- // This block covers case A in
- // https://drafts.csswg.org/css-flexbox/#algo-main-item.
- const Length& length_to_resolve =
- flex_basis.IsAuto() ? specified_length_in_main_axis : flex_basis;
- DCHECK(!length_to_resolve.IsAuto());
-
- if (MainAxisIsInlineAxis(child)) {
- flex_base_border_box = ResolveMainInlineLength(
- child_space, child_style, border_padding_in_child_writing_mode,
- intrinsic_sizes_border_box, length_to_resolve);
- } else {
- // Flex container's main axis is in child's block direction. Child's
- // flex basis is in child's block direction.
- flex_base_border_box = ResolveMainBlockLength(
- child_space, child_style, border_padding_in_child_writing_mode,
- length_to_resolve, fragment_in_child_writing_mode.BlockSize(),
- LengthResolvePhase::kLayout);
+ LayoutUnit cross_axis_border_padding =
+ is_horizontal_flow_ ? physical_border_padding.VerticalSum()
+ : physical_border_padding.HorizontalSum();
+
+ base::Optional<MinMaxSizes> min_max_size;
+ auto MinMaxSizesFunc = [&]() -> MinMaxSizes {
+ if (!min_max_size) {
+ if (child.Style().OverflowBlockDirection() == EOverflow::kAuto) {
+ // Ensure this child has been laid out so its auto scrollbars are
+ // included in its intrinsic sizes.
+ child.Layout(flex_basis_space);
+ }
+ // We want the child's min/max size in its writing mode, not ours.
+ // We'll only ever use it if the child's inline axis is our main axis.
+ min_max_size = child.ComputeMinMaxSizes(
+ child_style.GetWritingMode(),
+ MinMaxSizesInput(content_box_size_.block_size), &flex_basis_space);
}
- }
+ return *min_max_size;
+ };
- // Spec calls this "flex base size"
- // https://www.w3.org/TR/css-flexbox-1/#algo-main-item
- // Blink's FlexibleBoxAlgorithm expects it to be content + scrollbar widths,
- // but no padding or border.
- LayoutUnit flex_base_content_size =
- flex_base_border_box - main_axis_border_padding;
-
- NGPhysicalBoxStrut physical_child_margins =
- ComputePhysicalMargins(child_space, child_style);
- // Set margin because FlexibleBoxAlgorithm reads it from legacy.
- child.GetLayoutBox()->SetMargin(physical_child_margins);
-
- LayoutUnit main_axis_margin = is_horizontal_flow_
- ? physical_child_margins.HorizontalSum()
- : physical_child_margins.VerticalSum();
-
- MinMaxSize min_max_sizes_in_main_axis_direction{LayoutUnit(),
- LayoutUnit::Max()};
- MinMaxSize min_max_sizes_in_cross_axis_direction{LayoutUnit(),
+ MinMaxSizes min_max_sizes_in_main_axis_direction{main_axis_border_padding,
LayoutUnit::Max()};
+ MinMaxSizes min_max_sizes_in_cross_axis_direction{LayoutUnit(),
+ LayoutUnit::Max()};
const Length& max_property_in_main_axis = is_horizontal_flow_
? child.Style().MaxWidth()
: child.Style().MaxHeight();
@@ -341,112 +458,282 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
: child.Style().MinWidth();
if (MainAxisIsInlineAxis(child)) {
min_max_sizes_in_main_axis_direction.max_size = ResolveMaxInlineLength(
- child_space, child_style, border_padding_in_child_writing_mode,
- intrinsic_sizes_border_box, max_property_in_main_axis,
+ flex_basis_space, child_style, border_padding_in_child_writing_mode,
+ MinMaxSizesFunc, max_property_in_main_axis,
LengthResolvePhase::kLayout);
- min_max_sizes_in_cross_axis_direction.max_size =
- ResolveMaxBlockLength(child_space, child_style,
- border_scrollbar_padding_in_child_writing_mode,
- max_property_in_cross_axis,
- fragment_in_child_writing_mode.BlockSize(),
- LengthResolvePhase::kLayout);
- min_max_sizes_in_cross_axis_direction.min_size =
- ResolveMinBlockLength(child_space, child_style,
- border_scrollbar_padding_in_child_writing_mode,
- min_property_in_cross_axis,
- fragment_in_child_writing_mode.BlockSize(),
- LengthResolvePhase::kLayout);
+ min_max_sizes_in_cross_axis_direction.max_size = ResolveMaxBlockLength(
+ flex_basis_space, child_style, border_padding_in_child_writing_mode,
+ max_property_in_cross_axis, LengthResolvePhase::kLayout);
+ min_max_sizes_in_cross_axis_direction.min_size = ResolveMinBlockLength(
+ flex_basis_space, child_style, border_padding_in_child_writing_mode,
+ min_property_in_cross_axis, LengthResolvePhase::kLayout);
} else {
min_max_sizes_in_main_axis_direction.max_size = ResolveMaxBlockLength(
- child_space, child_style, border_padding_in_child_writing_mode,
- max_property_in_main_axis, fragment_in_child_writing_mode.BlockSize(),
- LengthResolvePhase::kLayout);
+ flex_basis_space, child_style, border_padding_in_child_writing_mode,
+ max_property_in_main_axis, LengthResolvePhase::kLayout);
min_max_sizes_in_cross_axis_direction.max_size = ResolveMaxInlineLength(
- child_space, child_style,
- border_scrollbar_padding_in_child_writing_mode,
- intrinsic_sizes_border_box, max_property_in_cross_axis,
+ flex_basis_space, child_style, border_padding_in_child_writing_mode,
+ MinMaxSizesFunc, max_property_in_cross_axis,
LengthResolvePhase::kLayout);
min_max_sizes_in_cross_axis_direction.min_size = ResolveMinInlineLength(
- child_space, child_style,
- border_scrollbar_padding_in_child_writing_mode,
- intrinsic_sizes_border_box, min_property_in_cross_axis,
+ flex_basis_space, child_style, border_padding_in_child_writing_mode,
+ MinMaxSizesFunc, min_property_in_cross_axis,
LengthResolvePhase::kLayout);
}
+ base::Optional<LayoutUnit> intrinsic_block_size;
+ auto IntrinsicBlockSizeFunc = [&]() -> LayoutUnit {
+ if (!intrinsic_block_size) {
+ NGConstraintSpace child_space = BuildSpaceForIntrinsicBlockSize(
+ child, physical_child_margins,
+ min_max_sizes_in_cross_axis_direction);
+ scoped_refptr<const NGLayoutResult> layout_result =
+ child.Layout(child_space, /* break_token */ nullptr);
+ intrinsic_block_size = layout_result->IntrinsicBlockSize();
+ }
+ return *intrinsic_block_size;
+ };
+
+ // The logic that calculates flex_base_border_box assumes that the used
+ // value of the flex-basis property is either definite or 'content'.
+ LayoutUnit flex_base_border_box;
+ const Length& specified_length_in_main_axis =
+ is_horizontal_flow_ ? child_style.Width() : child_style.Height();
+ const Length& flex_basis = child_style.FlexBasis();
+ Length length_to_resolve = Length::Auto();
+ if (flex_basis.IsAuto()) {
+ if (!is_column_ || IsItemMainSizeDefinite(child))
+ length_to_resolve = specified_length_in_main_axis;
+ } else if (IsItemFlexBasisDefinite(child)) {
+ length_to_resolve = flex_basis;
+ }
+
+ if (length_to_resolve.IsAuto()) {
+ // This block means that the used flex-basis is 'content'. In here we
+ // implement parts B,C,D,E of 9.2.3
+ // https://drafts.csswg.org/css-flexbox/#algo-main-item
+ const Length& cross_axis_length =
+ is_horizontal_flow_ ? child.Style().Height() : child.Style().Width();
+ if (child.HasAspectRatio() &&
+ (IsItemCrossAxisLengthDefinite(child, cross_axis_length))) {
+ // This is Part B of 9.2.3
+ // https://drafts.csswg.org/css-flexbox/#algo-main-item It requires that
+ // the item has a definite cross size.
+ //
+ // But for determining the flex-basis of aspect ratio items, both legacy
+ // and FF both ignore part of the flex spec that has a more lenient
+ // definition of definite.
+ // https://drafts.csswg.org/css-flexbox/#definite says "If a single-line
+ // flex container has a definite cross size, the outer cross size of any
+ // stretched flex items is the flex container's inner cross size
+ // (clamped to the flex item's min and max cross size) and is considered
+ // definite". But when this happens, neither legacy nor firefox use the
+ // container's cross size to calculate the item's main size, they just
+ // fall to block E. E.g. Legacy and FF show a 16x100 green square
+ // instead of a 100x100 green square for
+ // https://jsfiddle.net/dgrogan/djh5wu0x/1/. I think it should be
+ // 100x100.
+ LayoutUnit cross_size;
+ if (MainAxisIsInlineAxis(child)) {
+ cross_size = ResolveMainBlockLength(
+ flex_basis_space, child_style,
+ border_padding_in_child_writing_mode, cross_axis_length,
+ kIndefiniteSize, LengthResolvePhase::kLayout);
+ } else {
+ cross_size =
+ ResolveMainInlineLength(flex_basis_space, child_style,
+ border_padding_in_child_writing_mode,
+ MinMaxSizesFunc, cross_axis_length);
+ }
+ cross_size = min_max_sizes_in_cross_axis_direction.ClampSizeToMinAndMax(
+ cross_size);
+ flex_base_border_box =
+ LayoutUnit(cross_size * GetMainOverCrossAspectRatio(child));
+ } else if (MainAxisIsInlineAxis(child)) {
+ // We're now in parts C, D, and E for what are usually (horizontal-tb
+ // containers AND children) row flex containers. I _think_ the C and D
+ // cases are correctly handled by this code, which was originally
+ // written for case E.
+ if (child.HasAspectRatio()) {
+ // Legacy uses child.PreferredLogicalWidths() for this case, which
+ // is not exactly correct.
+ // TODO(dgrogan): Replace with a variant of ComputeReplacedSize that
+ // ignores min-width, width, max-width.
+ flex_base_border_box =
+ child.GetLayoutBox()->PreferredLogicalWidths().max_size;
+ } else {
+ flex_base_border_box = MinMaxSizesFunc().max_size;
+ }
+ } else {
+ // Parts C, D, and E for what are usually column flex containers.
+ //
+ // This is the post-layout height for aspect-ratio items, which matches
+ // legacy but isn't always correct.
+ // TODO(dgrogan): Replace with a variant of ComputeReplacedSize that
+ // ignores min-height, height, max-height.
+ flex_base_border_box = IntrinsicBlockSizeFunc();
+ }
+ } else {
+ // Part A of 9.2.3 https://drafts.csswg.org/css-flexbox/#algo-main-item
+ if (MainAxisIsInlineAxis(child)) {
+ flex_base_border_box = ResolveMainInlineLength(
+ flex_basis_space, child_style, border_padding_in_child_writing_mode,
+ MinMaxSizesFunc, length_to_resolve);
+ } else {
+ // Flex container's main axis is in child's block direction. Child's
+ // flex basis is in child's block direction.
+ flex_base_border_box = ResolveMainBlockLength(
+ flex_basis_space, child_style, border_padding_in_child_writing_mode,
+ length_to_resolve, IntrinsicBlockSizeFunc,
+ LengthResolvePhase::kLayout);
+ }
+ }
+
+ // Spec calls this "flex base size"
+ // https://www.w3.org/TR/css-flexbox-1/#algo-main-item
+ // Blink's FlexibleBoxAlgorithm expects it to be content + scrollbar widths,
+ // but no padding or border.
+ // The ClampNegativeToZero is needed for the last canvas element in
+ // flexbox-flex-basis-content-001a.html. It's possibly only needed because
+ // we don't properly account for borders+padding when multiplying by the
+ // aspect ratio.
+ LayoutUnit flex_base_content_size =
+ (flex_base_border_box - main_axis_border_padding).ClampNegativeToZero();
+
const Length& min = is_horizontal_flow_ ? child.Style().MinWidth()
: child.Style().MinHeight();
+ // TODO(dgrogan): min.IsIntrinsic should enter this block when it's in the
+ // item's block direction.
if (min.IsAuto()) {
if (algorithm_->ShouldApplyMinSizeAutoForChild(*child.GetLayoutBox())) {
- // TODO(dgrogan): Do the aspect ratio parts of
- // https://www.w3.org/TR/css-flexbox-1/#min-size-auto
-
- LayoutUnit content_size_suggestion =
- MainAxisIsInlineAxis(child) ? intrinsic_sizes_border_box.min_size
- : layout_result->IntrinsicBlockSize();
- content_size_suggestion =
- std::min(content_size_suggestion,
- min_max_sizes_in_main_axis_direction.max_size);
-
- if (child.MayHaveAspectRatio()) {
- // TODO(dgrogan): We're including borders/padding in both
- // content_size_suggestion and min_max_sizes_in_cross_axis_direction.
- // Maybe we need to multiply the content size by the aspect ratio and
- // then apply the border/padding from the other axis inside the
- // Adjust* function. Test legacy/firefox. Start with
- // https://jsfiddle.net/dgrogan/9uyg3aro/
- content_size_suggestion =
- AdjustChildSizeForAspectRatioCrossAxisMinAndMax(
- child, content_size_suggestion,
- min_max_sizes_in_cross_axis_direction.min_size,
- min_max_sizes_in_cross_axis_direction.max_size);
- }
+ // TODO(dgrogan): This should probably apply to column flexboxes also,
+ // but that's not what legacy does.
+ if (child.IsTable() && !is_column_) {
+ MinMaxSizes table_preferred_widths =
+ ComputeMinAndMaxContentContribution(
+ Style(), child,
+ MinMaxSizesInput(child_percentage_size_.block_size));
+ min_max_sizes_in_main_axis_direction.min_size =
+ table_preferred_widths.min_size;
+ } else {
+ LayoutUnit content_size_suggestion;
+ if (MainAxisIsInlineAxis(child)) {
+ content_size_suggestion = MinMaxSizesFunc().min_size;
+ } else {
+ LayoutUnit intrinsic_block_size;
+ if (child.HasAspectRatio()) {
+ base::Optional<LayoutUnit> computed_inline_size;
+ base::Optional<LayoutUnit> computed_block_size;
+ child.IntrinsicSize(&computed_inline_size, &computed_block_size);
+
+ // The 150 is for elements that have an aspect ratio but no size,
+ // which SVG can have (maybe others?).
+ intrinsic_block_size =
+ computed_block_size.value_or(LayoutUnit(150));
+ } else {
+ intrinsic_block_size = IntrinsicBlockSizeFunc();
+ }
+ content_size_suggestion = intrinsic_block_size;
+ }
- LayoutUnit specified_size_suggestion(LayoutUnit::Max());
- // If the item’s computed main size property is definite, then the
- // specified size suggestion is that size.
- if (MainAxisIsInlineAxis(child)) {
- if (!specified_length_in_main_axis.IsAuto()) {
- // TODO(dgrogan): Optimization opportunity: we may have already
- // resolved specified_length_in_main_axis in the flex basis
- // calculation. Reuse that if possible.
- specified_size_suggestion = ResolveMainInlineLength(
- child_space, child_style, border_padding_in_child_writing_mode,
- intrinsic_sizes_border_box, specified_length_in_main_axis);
+
+ if (child.HasAspectRatio()) {
+ // TODO(dgrogan): We're including borders/padding in both
+ // content_size_suggestion and
+ // min_max_sizes_in_cross_axis_direction. Maybe we need to multiply
+ // the content size by the aspect ratio and then apply the
+ // border/padding from the other axis inside the Adjust* function.
+ // Test legacy/firefox. Start with
+ // https://jsfiddle.net/dgrogan/9uyg3aro/
+ content_size_suggestion =
+ AdjustChildSizeForAspectRatioCrossAxisMinAndMax(
+ child, content_size_suggestion,
+ min_max_sizes_in_cross_axis_direction.min_size,
+ min_max_sizes_in_cross_axis_direction.max_size);
+ }
+
+ LayoutUnit specified_size_suggestion = LayoutUnit::Max();
+ // If the item’s computed main size property is definite, then the
+ // specified size suggestion is that size.
+ if (MainAxisIsInlineAxis(child)) {
+ if (!specified_length_in_main_axis.IsAuto()) {
+ // TODO(dgrogan): Optimization opportunity: we may have already
+ // resolved specified_length_in_main_axis in the flex basis
+ // calculation. Reuse that if possible.
+ specified_size_suggestion = ResolveMainInlineLength(
+ flex_basis_space, child_style,
+ border_padding_in_child_writing_mode, MinMaxSizesFunc,
+ specified_length_in_main_axis);
+ }
+ } else if (!BlockLengthUnresolvable(flex_basis_space,
+ specified_length_in_main_axis,
+ LengthResolvePhase::kLayout)) {
+ specified_size_suggestion = ResolveMainBlockLength(
+ flex_basis_space, child_style,
+ border_padding_in_child_writing_mode,
+ specified_length_in_main_axis, IntrinsicBlockSizeFunc,
+ LengthResolvePhase::kLayout);
+ DCHECK_NE(specified_size_suggestion, kIndefiniteSize);
}
- } else if (!BlockLengthUnresolvable(child_space,
- specified_length_in_main_axis,
- LengthResolvePhase::kLayout)) {
- specified_size_suggestion = ResolveMainBlockLength(
- child_space, child_style, border_padding_in_child_writing_mode,
- specified_length_in_main_axis,
- layout_result->IntrinsicBlockSize(), LengthResolvePhase::kLayout);
- DCHECK_NE(specified_size_suggestion, kIndefiniteSize);
- }
- // Spec says to clamp specified_size_suggestion by max size but because
- // content_size_suggestion already is, and we take the min of those
- // two, we don't need to clamp specified_size_suggestion.
- // https://github.com/w3c/csswg-drafts/issues/3669
- min_max_sizes_in_main_axis_direction.min_size =
- std::min(specified_size_suggestion, content_size_suggestion);
+ LayoutUnit transferred_size_suggestion = LayoutUnit::Max();
+ if (specified_size_suggestion == LayoutUnit::Max() &&
+ child.HasAspectRatio()) {
+ const Length& cross_axis_length = is_horizontal_flow_
+ ? child_style.Height()
+ : child_style.Width();
+ if (IsItemCrossAxisLengthDefinite(child, cross_axis_length)) {
+ LayoutUnit cross_axis_size;
+ if (MainAxisIsInlineAxis(child)) {
+ cross_axis_size = ResolveMainBlockLength(
+ flex_basis_space, child_style,
+ border_padding_in_child_writing_mode, cross_axis_length,
+ kIndefiniteSize, LengthResolvePhase::kLayout);
+ DCHECK_NE(cross_axis_size, kIndefiniteSize);
+ } else {
+ cross_axis_size = ResolveMainInlineLength(
+ flex_basis_space, child_style,
+ border_padding_in_child_writing_mode, MinMaxSizesFunc,
+ cross_axis_length);
+ }
+ double ratio = GetMainOverCrossAspectRatio(child);
+ transferred_size_suggestion = LayoutUnit(
+ ratio *
+ min_max_sizes_in_cross_axis_direction.ClampSizeToMinAndMax(
+ cross_axis_size));
+ }
+ }
+
+ DCHECK(specified_size_suggestion == LayoutUnit::Max() ||
+ transferred_size_suggestion == LayoutUnit::Max());
+
+ min_max_sizes_in_main_axis_direction.min_size =
+ std::min({specified_size_suggestion, content_size_suggestion,
+ transferred_size_suggestion,
+ min_max_sizes_in_main_axis_direction.max_size});
+ }
}
} else if (MainAxisIsInlineAxis(child)) {
min_max_sizes_in_main_axis_direction.min_size = ResolveMinInlineLength(
- child_space, child_style, border_padding_in_child_writing_mode,
- intrinsic_sizes_border_box, min, LengthResolvePhase::kLayout);
+ flex_basis_space, child_style, border_padding_in_child_writing_mode,
+ MinMaxSizesFunc, min, LengthResolvePhase::kLayout);
} else {
min_max_sizes_in_main_axis_direction.min_size = ResolveMinBlockLength(
- child_space, child_style, border_padding_in_child_writing_mode, min,
- fragment_in_child_writing_mode.BlockSize(),
- LengthResolvePhase::kLayout);
+ flex_basis_space, child_style, border_padding_in_child_writing_mode,
+ min, LengthResolvePhase::kLayout);
}
- // TODO(dgrogan): Should this include scrollbar?
- min_max_sizes_in_main_axis_direction -= main_axis_border_scrollbar_padding;
+ min_max_sizes_in_main_axis_direction -= main_axis_border_padding;
+ DCHECK_GE(min_max_sizes_in_main_axis_direction.min_size, 0);
+ DCHECK_GE(min_max_sizes_in_main_axis_direction.max_size, 0);
+
+ // TODO(dgrogan): Should min_max_sizes_in_cross_axis_direction include
+ // cross_axis_border_padding?
algorithm_
- ->emplace_back(child.GetLayoutBox(), flex_base_content_size,
+ ->emplace_back(nullptr, child.Style(), flex_base_content_size,
min_max_sizes_in_main_axis_direction,
min_max_sizes_in_cross_axis_direction,
- main_axis_border_padding, main_axis_margin)
+ main_axis_border_padding, cross_axis_border_padding,
+ physical_child_margins)
.ng_input_node = child;
}
}
@@ -457,33 +744,16 @@ NGFlexLayoutAlgorithm::AdjustChildSizeForAspectRatioCrossAxisMinAndMax(
LayoutUnit content_suggestion,
LayoutUnit cross_min,
LayoutUnit cross_max) {
- DCHECK(child.MayHaveAspectRatio());
+ DCHECK(child.HasAspectRatio());
+
+ double ratio = GetMainOverCrossAspectRatio(child);
// Clamp content_suggestion by any definite min and max cross size properties
// converted through the aspect ratio.
-
- base::Optional<LayoutUnit> computed_inline_size;
- base::Optional<LayoutUnit> computed_block_size;
- LogicalSize aspect_ratio;
-
- child.IntrinsicSize(&computed_inline_size, &computed_block_size,
- &aspect_ratio);
-
- // TODO(dgrogan): Should we quit here if only the denominator is 0?
- if (aspect_ratio.inline_size == 0 || aspect_ratio.block_size == 0)
- return content_suggestion;
-
- double ratio = aspect_ratio.inline_size / aspect_ratio.block_size;
-
- // Multiplying by ratio will take something in the item's block axis and
- // convert it to the inline axis. We want to convert from cross size to main
- // size. If block axis and cross axis are the same, then we already have what
- // we need. Otherwise we need to use the reciprocal.
- if (!MainAxisIsInlineAxis(child))
- ratio = 1 / ratio;
-
const Length& cross_max_length = is_horizontal_flow_
? child.Style().MaxHeight()
: child.Style().MaxWidth();
+ // TODO(dgrogan): No tests fail if we unconditionally apply max_main_length.
+ // Either add a test that needs it or remove it.
if (IsItemCrossAxisLengthDefinite(child, cross_max_length)) {
LayoutUnit max_main_length = LayoutUnit(cross_max * ratio);
content_suggestion = std::min(max_main_length, content_suggestion);
@@ -492,6 +762,8 @@ NGFlexLayoutAlgorithm::AdjustChildSizeForAspectRatioCrossAxisMinAndMax(
const Length& cross_min_length = is_horizontal_flow_
? child.Style().MinHeight()
: child.Style().MinWidth();
+ // TODO(dgrogan): Same as above with min_main_length here -- it may be
+ // unneeded or untested.
if (IsItemCrossAxisLengthDefinite(child, cross_min_length)) {
LayoutUnit min_main_length = LayoutUnit(cross_min * ratio);
content_suggestion = std::max(min_main_length, content_suggestion);
@@ -500,15 +772,7 @@ NGFlexLayoutAlgorithm::AdjustChildSizeForAspectRatioCrossAxisMinAndMax(
}
scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
- border_box_size_ = container_builder_.InitialBorderBoxSize();
- content_box_size_ =
- ShrinkAvailableSize(border_box_size_, border_scrollbar_padding_);
- child_percentage_size_ = CalculateChildPercentageSize(
- ConstraintSpace(), Node(), content_box_size_);
-
- const LayoutUnit line_break_length = MainAxisContentExtent(LayoutUnit::Max());
- algorithm_.emplace(&Style(), line_break_length);
-
+ PaintLayerScrollableArea::DelayScrollOffsetClampScope delay_clamp_scope;
ConstructAndAppendFlexItems();
LayoutUnit main_axis_start_offset;
@@ -542,22 +806,29 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
for (wtf_size_t i = 0; i < line->line_items.size(); ++i) {
FlexItem& flex_item = line->line_items[i];
- WritingMode child_writing_mode =
- flex_item.ng_input_node.Style().GetWritingMode();
+ const ComputedStyle& child_style = flex_item.ng_input_node.Style();
NGConstraintSpaceBuilder space_builder(ConstraintSpace(),
- child_writing_mode,
+ child_style.GetWritingMode(),
/* is_new_fc */ true);
SetOrthogonalFallbackInlineSizeIfNeeded(Style(), flex_item.ng_input_node,
&space_builder);
- space_builder.SetTextDirection(
- flex_item.ng_input_node.Style().Direction());
+ space_builder.SetTextDirection(child_style.Direction());
+ space_builder.SetIsPaintedAtomically(true);
LogicalSize available_size;
+ NGBoxStrut margins = flex_item.physical_margins.ConvertToLogical(
+ ConstraintSpace().GetWritingMode(), Style().Direction());
if (is_column_) {
available_size.inline_size = content_box_size_.inline_size;
available_size.block_size =
flex_item.flexed_content_size + flex_item.main_axis_border_padding;
space_builder.SetIsFixedBlockSize(true);
+ if (WillChildCrossSizeBeContainerCrossSize(flex_item.ng_input_node)) {
+ space_builder.SetIsFixedInlineSize(true);
+ available_size.inline_size = CalculateFixedCrossSize(
+ available_size.inline_size, flex_item.min_max_cross_sizes.value(),
+ margins.InlineSum());
+ }
// https://drafts.csswg.org/css-flexbox/#definite-sizes
// If the flex container has a definite main size, a flex item's
// post-flexing main size is treated as definite, even though it can
@@ -571,16 +842,22 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
flex_item.flexed_content_size + flex_item.main_axis_border_padding;
available_size.block_size = content_box_size_.block_size;
space_builder.SetIsFixedInlineSize(true);
- }
- if (WillChildCrossSizeBeContainerCrossSize(flex_item.ng_input_node)) {
- if (is_column_)
- space_builder.SetIsFixedInlineSize(true);
- else
+ if (WillChildCrossSizeBeContainerCrossSize(flex_item.ng_input_node)) {
space_builder.SetIsFixedBlockSize(true);
+ available_size.block_size = CalculateFixedCrossSize(
+ available_size.block_size, flex_item.min_max_cross_sizes.value(),
+ margins.BlockSum());
+ }
}
+ space_builder.SetNeedsBaseline(
+ ConstraintSpace().NeedsBaseline() ||
+ FlexLayoutAlgorithm::AlignmentForChild(Style(), child_style) ==
+ ItemPosition::kBaseline);
+
space_builder.SetAvailableSize(available_size);
space_builder.SetPercentageResolutionSize(child_percentage_size_);
+ space_builder.SetReplacedPercentageResolutionSize(child_percentage_size_);
// https://drafts.csswg.org/css-flexbox/#algo-cross-item
// Determine the hypothetical cross size of each item by performing layout
@@ -592,6 +869,10 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
NGConstraintSpace child_space = space_builder.ToConstraintSpace();
flex_item.layout_result =
flex_item.ng_input_node.Layout(child_space, nullptr /*break token*/);
+
+ // TODO(layout-dev): Handle abortions caused by block fragmentation.
+ DCHECK_EQ(flex_item.layout_result->Status(), NGLayoutResult::kSuccess);
+
flex_item.cross_axis_size =
is_horizontal_flow_
? flex_item.layout_result->PhysicalFragment().Size().height
@@ -627,12 +908,13 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
}
void NGFlexLayoutAlgorithm::ApplyStretchAlignmentToChild(FlexItem& flex_item) {
- WritingMode child_writing_mode =
- flex_item.ng_input_node.Style().GetWritingMode();
- NGConstraintSpaceBuilder space_builder(ConstraintSpace(), child_writing_mode,
+ const ComputedStyle& child_style = flex_item.ng_input_node.Style();
+ NGConstraintSpaceBuilder space_builder(ConstraintSpace(),
+ child_style.GetWritingMode(),
/* is_new_fc */ true);
SetOrthogonalFallbackInlineSizeIfNeeded(Style(), flex_item.ng_input_node,
&space_builder);
+ space_builder.SetIsPaintedAtomically(true);
LogicalSize available_size(
flex_item.flexed_content_size + flex_item.main_axis_border_padding,
@@ -644,9 +926,16 @@ void NGFlexLayoutAlgorithm::ApplyStretchAlignmentToChild(FlexItem& flex_item) {
space_builder.SetIsFixedBlockSizeIndefinite(true);
}
}
- space_builder.SetTextDirection(flex_item.ng_input_node.Style().Direction());
+
+ space_builder.SetNeedsBaseline(
+ ConstraintSpace().NeedsBaseline() ||
+ FlexLayoutAlgorithm::AlignmentForChild(Style(), child_style) ==
+ ItemPosition::kBaseline);
+
+ space_builder.SetTextDirection(child_style.Direction());
space_builder.SetAvailableSize(available_size);
space_builder.SetPercentageResolutionSize(child_percentage_size_);
+ space_builder.SetReplacedPercentageResolutionSize(child_percentage_size_);
space_builder.SetIsFixedInlineSize(true);
space_builder.SetIsFixedBlockSize(true);
NGConstraintSpace child_space = space_builder.ToConstraintSpace();
@@ -687,6 +976,9 @@ void NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() {
border_scrollbar_padding_.block_start);
}
+ base::Optional<LayoutUnit> fallback_baseline;
+
+ LayoutUnit overflow_block_size;
for (FlexLine& line_context : line_contexts) {
for (wtf_size_t child_number = 0;
child_number < line_context.line_items.size(); ++child_number) {
@@ -695,6 +987,9 @@ void NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() {
if (DoesItemStretch(flex_item.ng_input_node))
ApplyStretchAlignmentToChild(flex_item);
+ const auto& physical_fragment = To<NGPhysicalBoxFragment>(
+ flex_item.layout_result->PhysicalFragment());
+
// flex_item.desired_location stores the main axis offset in X and the
// cross axis offset in Y. But AddChild wants offset from parent
// rectangle, so we have to transpose for columns. AddChild takes care of
@@ -702,16 +997,70 @@ void NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() {
LayoutPoint location = is_column_
? flex_item.desired_location.TransposedPoint()
: flex_item.desired_location;
- container_builder_.AddChild(flex_item.layout_result->PhysicalFragment(),
+
+ NGBoxFragment fragment(ConstraintSpace().GetWritingMode(),
+ ConstraintSpace().Direction(), physical_fragment);
+ // Only propagate baselines from children on the first flex-line.
+ if (&line_context == line_contexts.begin()) {
+ PropagateBaselineFromChild(flex_item, fragment, location.Y(),
+ &fallback_baseline);
+ }
+
+ container_builder_.AddChild(physical_fragment,
{location.X(), location.Y()});
+
+ flex_item.ng_input_node.StoreMargins(flex_item.physical_margins);
+
+ LayoutUnit margin_block_end =
+ flex_item.physical_margins
+ .ConvertToLogical(ConstraintSpace().GetWritingMode(),
+ ConstraintSpace().Direction())
+ .block_end;
+ overflow_block_size =
+ std::max(overflow_block_size,
+ location.Y() + fragment.BlockSize() + margin_block_end);
}
}
+
+ container_builder_.SetOverflowBlockSize(overflow_block_size +
+ border_scrollbar_padding_.block_end);
+
+ // Set the baseline to the fallback, if we didn't find any children with
+ // baseline alignment.
+ if (!container_builder_.Baseline() && fallback_baseline)
+ container_builder_.SetBaseline(*fallback_baseline);
+}
+
+void NGFlexLayoutAlgorithm::PropagateBaselineFromChild(
+ const FlexItem& flex_item,
+ const NGBoxFragment& fragment,
+ LayoutUnit block_offset,
+ base::Optional<LayoutUnit>* fallback_baseline) {
+ // Check if we've already found an appropriate baseline.
+ if (container_builder_.Baseline())
+ return;
+
+ LayoutUnit baseline_offset =
+ block_offset + fragment.Baseline().value_or(fragment.BlockSize());
+
+ // We prefer a baseline from a child with baseline alignment, and no
+ // auto-margins in the cross axis (even if we have to synthesize the
+ // baseline).
+ if (FlexLayoutAlgorithm::AlignmentForChild(Style(), flex_item.style) ==
+ ItemPosition::kBaseline &&
+ !flex_item.HasAutoMarginsInCrossAxis()) {
+ container_builder_.SetBaseline(baseline_offset);
+ return;
+ }
+
+ // Set the fallback baseline if it doesn't have a value yet.
+ *fallback_baseline = fallback_baseline->value_or(baseline_offset);
}
-base::Optional<MinMaxSize> NGFlexLayoutAlgorithm::ComputeMinMaxSize(
- const MinMaxSizeInput& input) const {
- base::Optional<MinMaxSize> sizes = CalculateMinMaxSizesIgnoringChildren(
- Node(), border_scrollbar_padding_, input.size_type);
+base::Optional<MinMaxSizes> NGFlexLayoutAlgorithm::ComputeMinMaxSizes(
+ const MinMaxSizesInput& input) const {
+ base::Optional<MinMaxSizes> sizes =
+ CalculateMinMaxSizesIgnoringChildren(Node(), border_scrollbar_padding_);
if (sizes)
return sizes;
@@ -721,21 +1070,15 @@ base::Optional<MinMaxSize> NGFlexLayoutAlgorithm::ComputeMinMaxSize(
ConstraintSpace(), Node(), border_padding_,
input.percentage_resolution_block_size);
- // Use default MinMaxSizeInput:
- // - Children of flexbox ignore any specified float properties, so children
- // never have to take floated siblings into account, and external floats
- // don't make it through the new formatting context that flexbox
- // establishes.
- // - We want the child's border box MinMaxSize, which is the default.
- MinMaxSizeInput child_input(child_percentage_resolution_block_size);
-
- for (NGLayoutInputNode generic_child = Node().FirstChild(); generic_child;
- generic_child = generic_child.NextSibling()) {
- auto child = To<NGBlockNode>(generic_child);
+ MinMaxSizesInput child_input(child_percentage_resolution_block_size);
+
+ NGFlexChildIterator iterator(Node());
+ for (NGBlockNode child = iterator.NextChild(); child;
+ child = iterator.NextChild()) {
if (child.IsOutOfFlowPositioned())
continue;
- MinMaxSize child_min_max_sizes =
+ MinMaxSizes child_min_max_sizes =
ComputeMinAndMaxContentContribution(Style(), child, child_input);
NGBoxStrut child_margins = ComputeMinMaxMargins(Style(), child);
child_min_max_sizes += child_margins.InlineSum();
@@ -744,7 +1087,7 @@ base::Optional<MinMaxSize> NGFlexLayoutAlgorithm::ComputeMinMaxSize(
sizes->max_size = std::max(sizes->max_size, child_min_max_sizes.max_size);
} else {
sizes->max_size += child_min_max_sizes.max_size;
- if (IsMultiline()) {
+ if (algorithm_->IsMultiline()) {
sizes->min_size =
std::max(sizes->min_size, child_min_max_sizes.min_size);
} else {
@@ -757,15 +1100,8 @@ base::Optional<MinMaxSize> NGFlexLayoutAlgorithm::ComputeMinMaxSize(
// Due to negative margins, it is possible that we calculated a negative
// intrinsic width. Make sure that we never return a negative width.
sizes->Encompass(LayoutUnit());
-
- if (input.size_type == NGMinMaxSizeType::kBorderBoxSize)
- *sizes += border_scrollbar_padding_.InlineSum();
-
+ *sizes += border_scrollbar_padding_.InlineSum();
return sizes;
}
-bool NGFlexLayoutAlgorithm::IsMultiline() const {
- return Style().FlexWrap() != EFlexWrap::kNowrap;
-}
-
} // namespace blink