diff options
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.cc | 858 |
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 |