| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "core/layout/GridTrackSizingAlgorithm.h" |
| |
| #include "core/layout/Grid.h" |
| #include "core/layout/GridLayoutUtils.h" |
| #include "core/layout/LayoutGrid.h" |
| #include "platform/LengthFunctions.h" |
| |
| namespace blink { |
| |
| class GridSizingData; |
| |
| LayoutUnit GridTrack::BaseSize() const { |
| DCHECK(IsGrowthLimitBiggerThanBaseSize()); |
| return base_size_; |
| } |
| |
| LayoutUnit GridTrack::GrowthLimit() const { |
| DCHECK(IsGrowthLimitBiggerThanBaseSize()); |
| DCHECK(!growth_limit_cap_ || growth_limit_cap_.value() >= growth_limit_ || |
| base_size_ >= growth_limit_cap_.value()); |
| return growth_limit_; |
| } |
| |
| void GridTrack::SetBaseSize(LayoutUnit base_size) { |
| base_size_ = base_size; |
| EnsureGrowthLimitIsBiggerThanBaseSize(); |
| } |
| |
| void GridTrack::SetGrowthLimit(LayoutUnit growth_limit) { |
| growth_limit_ = |
| growth_limit == kInfinity |
| ? growth_limit |
| : std::min(growth_limit, growth_limit_cap_.value_or(growth_limit)); |
| EnsureGrowthLimitIsBiggerThanBaseSize(); |
| } |
| |
| bool GridTrack::InfiniteGrowthPotential() const { |
| return GrowthLimitIsInfinite() || infinitely_growable_; |
| } |
| |
| void GridTrack::SetPlannedSize(LayoutUnit planned_size) { |
| DCHECK(planned_size >= 0 || planned_size == kInfinity); |
| planned_size_ = planned_size; |
| } |
| |
| void GridTrack::SetSizeDuringDistribution(LayoutUnit size_during_distribution) { |
| DCHECK_GE(size_during_distribution, 0); |
| DCHECK(GrowthLimitIsInfinite() || GrowthLimit() >= size_during_distribution); |
| size_during_distribution_ = size_during_distribution; |
| } |
| |
| void GridTrack::GrowSizeDuringDistribution( |
| LayoutUnit size_during_distribution) { |
| DCHECK_GE(size_during_distribution, 0); |
| size_during_distribution_ += size_during_distribution; |
| } |
| |
| void GridTrack::SetInfinitelyGrowable(bool infinitely_growable) { |
| infinitely_growable_ = infinitely_growable; |
| } |
| |
| void GridTrack::SetGrowthLimitCap(Optional<LayoutUnit> growth_limit_cap) { |
| DCHECK(!growth_limit_cap || *growth_limit_cap >= 0); |
| growth_limit_cap_ = growth_limit_cap; |
| } |
| |
| bool GridTrack::IsGrowthLimitBiggerThanBaseSize() const { |
| return GrowthLimitIsInfinite() || growth_limit_ >= base_size_; |
| } |
| |
| void GridTrack::EnsureGrowthLimitIsBiggerThanBaseSize() { |
| if (growth_limit_ != kInfinity && growth_limit_ < base_size_) |
| growth_limit_ = base_size_; |
| } |
| |
| class IndefiniteSizeStrategy final : public GridTrackSizingAlgorithmStrategy { |
| public: |
| IndefiniteSizeStrategy(GridTrackSizingAlgorithm& algorithm) |
| : GridTrackSizingAlgorithmStrategy(algorithm) {} |
| |
| private: |
| LayoutUnit MinLogicalWidthForChild(LayoutBox&, |
| Length child_min_size, |
| LayoutUnit available_size) const override; |
| void LayoutGridItemForMinSizeComputation( |
| LayoutBox&, |
| bool override_size_has_changed) const override; |
| void MaximizeTracks(Vector<GridTrack>&, |
| Optional<LayoutUnit>& free_space) override; |
| double FindUsedFlexFraction(Vector<size_t>& flexible_sized_tracks_index, |
| GridTrackSizingDirection, |
| Optional<LayoutUnit> free_space) const override; |
| bool RecomputeUsedFlexFractionIfNeeded( |
| Vector<size_t>& flexible_sized_tracks_index, |
| double& flex_fraction, |
| Vector<LayoutUnit>& increments, |
| LayoutUnit& total_growth) const override; |
| LayoutUnit FreeSpaceForStretchAutoTracksStep() const override; |
| }; |
| |
| class DefiniteSizeStrategy final : public GridTrackSizingAlgorithmStrategy { |
| public: |
| DefiniteSizeStrategy(GridTrackSizingAlgorithm& algorithm) |
| : GridTrackSizingAlgorithmStrategy(algorithm) {} |
| |
| private: |
| LayoutUnit MinLogicalWidthForChild(LayoutBox&, |
| Length child_min_size, |
| LayoutUnit available_size) const override; |
| void LayoutGridItemForMinSizeComputation( |
| LayoutBox&, |
| bool override_size_has_changed) const override; |
| void MaximizeTracks(Vector<GridTrack>&, |
| Optional<LayoutUnit>& free_space) override; |
| double FindUsedFlexFraction(Vector<size_t>& flexible_sized_tracks_index, |
| GridTrackSizingDirection, |
| Optional<LayoutUnit> free_space) const override; |
| bool RecomputeUsedFlexFractionIfNeeded( |
| Vector<size_t>& flexible_sized_tracks_index, |
| double& flex_fraction, |
| Vector<LayoutUnit>& increments, |
| LayoutUnit& total_growth) const override { |
| return false; |
| } |
| LayoutUnit FreeSpaceForStretchAutoTracksStep() const override; |
| }; |
| |
| void GridTrackSizingAlgorithmStrategy::SetNeedsLayoutForChild( |
| LayoutBox& child) const { |
| if (algorithm_.is_in_perform_layout_) { |
| child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis); |
| } |
| } |
| |
| bool GridTrackSizingAlgorithmStrategy:: |
| HasOverrideContainingBlockContentSizeForChild( |
| const LayoutBox& child, |
| GridTrackSizingDirection direction) { |
| return direction == kForColumns |
| ? child.HasOverrideContainingBlockLogicalWidth() |
| : child.HasOverrideContainingBlockLogicalHeight(); |
| } |
| |
| GridTrackSizingAlgorithmStrategy::~GridTrackSizingAlgorithmStrategy() {} |
| |
| LayoutUnit |
| GridTrackSizingAlgorithmStrategy::OverrideContainingBlockContentSizeForChild( |
| const LayoutBox& child, |
| GridTrackSizingDirection direction) { |
| return direction == kForColumns |
| ? child.OverrideContainingBlockContentLogicalWidth() |
| : child.OverrideContainingBlockContentLogicalHeight(); |
| } |
| |
| bool GridTrackSizingAlgorithmStrategy:: |
| ShouldClearOverrideContainingBlockContentSizeForChild( |
| const LayoutGrid& grid, |
| const LayoutBox& child, |
| GridTrackSizingDirection direction) { |
| GridTrackSizingDirection child_inline_direction = |
| GridLayoutUtils::FlowAwareDirectionForChild(grid, child, kForColumns); |
| if (direction == child_inline_direction) { |
| return child.HasRelativeLogicalWidth() || |
| child.StyleRef().LogicalWidth().IsIntrinsicOrAuto(); |
| } |
| return child.HasRelativeLogicalHeight() || |
| child.StyleRef().LogicalHeight().IsIntrinsicOrAuto(); |
| } |
| |
| void GridTrackSizingAlgorithmStrategy:: |
| SetOverrideContainingBlockContentSizeForChild( |
| LayoutBox& child, |
| GridTrackSizingDirection direction, |
| LayoutUnit size) { |
| if (direction == kForColumns) |
| child.SetOverrideContainingBlockContentLogicalWidth(size); |
| else |
| child.SetOverrideContainingBlockContentLogicalHeight(size); |
| } |
| |
| LayoutUnit GridTrackSizingAlgorithm::AssumedRowsSizeForOrthogonalChild( |
| const LayoutBox& child) const { |
| DCHECK(GridLayoutUtils::IsOrthogonalChild(*layout_grid_, child)); |
| const GridSpan& span = grid_.GridItemSpan(child, kForRows); |
| LayoutUnit grid_area_size; |
| bool grid_area_is_indefinite = false; |
| LayoutUnit containing_block_available_size = |
| layout_grid_->ContainingBlockLogicalHeightForContent( |
| kExcludeMarginBorderPadding); |
| for (auto track_position : span) { |
| GridLength max_track_size = |
| GetGridTrackSize(kForRows, track_position).MaxTrackBreadth(); |
| if (max_track_size.IsContentSized() || max_track_size.IsFlex()) { |
| grid_area_is_indefinite = true; |
| } else { |
| grid_area_size += ValueForLength(max_track_size.length(), |
| containing_block_available_size); |
| } |
| } |
| |
| grid_area_size += |
| layout_grid_->GuttersSize(grid_, kForRows, span.StartLine(), |
| span.IntegerSpan(), AvailableSpace(kForRows)); |
| |
| return grid_area_is_indefinite |
| ? std::max(child.MaxPreferredLogicalWidth(), grid_area_size) |
| : grid_area_size; |
| } |
| |
| LayoutUnit GridTrackSizingAlgorithm::GridAreaBreadthForChild( |
| const LayoutBox& child, |
| GridTrackSizingDirection direction) { |
| if (direction == kForRows && sizing_state_ == kColumnSizingFirstIteration) |
| return AssumedRowsSizeForOrthogonalChild(child); |
| |
| Vector<GridTrack>& all_tracks = Tracks(direction); |
| const GridSpan& span = grid_.GridItemSpan(child, direction); |
| LayoutUnit grid_area_breadth; |
| for (const auto& track_position : span) |
| grid_area_breadth += all_tracks[track_position].BaseSize(); |
| |
| grid_area_breadth += |
| layout_grid_->GuttersSize(grid_, direction, span.StartLine(), |
| span.IntegerSpan(), AvailableSpace(direction)); |
| |
| return grid_area_breadth; |
| } |
| |
| bool GridTrackSizingAlgorithmStrategy:: |
| UpdateOverrideContainingBlockContentSizeForChild( |
| LayoutBox& child, |
| GridTrackSizingDirection direction, |
| Optional<LayoutUnit> override_size) const { |
| if (!override_size) |
| override_size = algorithm_.GridAreaBreadthForChild(child, direction); |
| if (HasOverrideContainingBlockContentSizeForChild(child, direction) && |
| OverrideContainingBlockContentSizeForChild(child, direction) == |
| override_size.value()) |
| return false; |
| |
| SetOverrideContainingBlockContentSizeForChild(child, direction, |
| override_size.value()); |
| return true; |
| } |
| |
| LayoutUnit GridTrackSizingAlgorithmStrategy::LogicalHeightForChild( |
| LayoutBox& child) const { |
| GridTrackSizingDirection child_block_direction = |
| GridLayoutUtils::FlowAwareDirectionForChild(*GetLayoutGrid(), child, |
| kForRows); |
| // If |child| has a relative block-axis size, we shouldn't let it override its |
| // intrinsic size, which is what we are interested in here. Thus we |
| // need to set the block-axis OverrideContainingBlock size to -1 (no possible |
| // resolution). |
| if (ShouldClearOverrideContainingBlockContentSizeForChild( |
| *GetLayoutGrid(), child, child_block_direction)) { |
| SetOverrideContainingBlockContentSizeForChild(child, child_block_direction, |
| LayoutUnit(-1)); |
| SetNeedsLayoutForChild(child); |
| } |
| |
| child.LayoutIfNeeded(); |
| |
| if (auto baseline_extent = ExtentForBaselineAlignment(child)) |
| return baseline_extent.value(); |
| |
| return child.LogicalHeight() + child.MarginLogicalHeight(); |
| } |
| |
| DISABLE_CFI_PERF |
| LayoutUnit GridTrackSizingAlgorithmStrategy::MinContentForChild( |
| LayoutBox& child) const { |
| GridTrackSizingDirection child_inline_direction = |
| GridLayoutUtils::FlowAwareDirectionForChild(*GetLayoutGrid(), child, |
| kForColumns); |
| if (Direction() == child_inline_direction) { |
| // FIXME: It's unclear if we should return the intrinsic width or the |
| // preferred width. |
| // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html |
| return child.MinPreferredLogicalWidth() + |
| GridLayoutUtils::MarginLogicalWidthForChild(*GetLayoutGrid(), child); |
| } |
| |
| if (Direction() == kForColumns && !AvailableSpace()) { |
| DCHECK(GridLayoutUtils::IsOrthogonalChild(*GetLayoutGrid(), child)); |
| if (auto baseline_extent = ExtentForBaselineAlignment(child)) |
| return baseline_extent.value(); |
| } |
| |
| if (UpdateOverrideContainingBlockContentSizeForChild( |
| child, child_inline_direction)) { |
| SetNeedsLayoutForChild(child); |
| } |
| return LogicalHeightForChild(child); |
| } |
| |
| DISABLE_CFI_PERF |
| LayoutUnit GridTrackSizingAlgorithmStrategy::MaxContentForChild( |
| LayoutBox& child) const { |
| GridTrackSizingDirection child_inline_direction = |
| GridLayoutUtils::FlowAwareDirectionForChild(*GetLayoutGrid(), child, |
| kForColumns); |
| if (Direction() == child_inline_direction) { |
| // FIXME: It's unclear if we should return the intrinsic width or the |
| // preferred width. |
| // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html |
| return child.MaxPreferredLogicalWidth() + |
| GridLayoutUtils::MarginLogicalWidthForChild(*GetLayoutGrid(), child); |
| } |
| |
| if (UpdateOverrideContainingBlockContentSizeForChild( |
| child, child_inline_direction)) { |
| SetNeedsLayoutForChild(child); |
| } |
| return LogicalHeightForChild(child); |
| } |
| |
| LayoutUnit GridTrackSizingAlgorithmStrategy::MinSizeForChild( |
| LayoutBox& child) const { |
| GridTrackSizingDirection child_inline_direction = |
| GridLayoutUtils::FlowAwareDirectionForChild(*GetLayoutGrid(), child, |
| kForColumns); |
| bool is_row_axis = Direction() == child_inline_direction; |
| const Length& child_size = is_row_axis ? child.StyleRef().LogicalWidth() |
| : child.StyleRef().LogicalHeight(); |
| const Length& child_min_size = is_row_axis |
| ? child.StyleRef().LogicalMinWidth() |
| : child.StyleRef().LogicalMinHeight(); |
| bool overflow_is_visible = |
| is_row_axis |
| ? child.StyleRef().OverflowInlineDirection() == EOverflow::kVisible |
| : child.StyleRef().OverflowBlockDirection() == EOverflow::kVisible; |
| if (!child_size.IsAuto() || (child_min_size.IsAuto() && overflow_is_visible)) |
| return MinContentForChild(child); |
| |
| LayoutUnit grid_area_size = |
| algorithm_.GridAreaBreadthForChild(child, child_inline_direction); |
| |
| if (is_row_axis) |
| return MinLogicalWidthForChild(child, child_min_size, grid_area_size); |
| |
| bool override_size_has_changed = |
| UpdateOverrideContainingBlockContentSizeForChild( |
| child, child_inline_direction, grid_area_size); |
| LayoutGridItemForMinSizeComputation(child, override_size_has_changed); |
| |
| return child.ComputeLogicalHeightUsing(kMinSize, child_min_size, |
| child.IntrinsicLogicalHeight()) + |
| child.MarginLogicalHeight() + child.ScrollbarLogicalHeight(); |
| } |
| |
| LayoutUnit GridTrackSizingAlgorithmStrategy::ComputeTrackBasedSize() const { |
| return algorithm_.ComputeTrackBasedSize(); |
| } |
| |
| double GridTrackSizingAlgorithmStrategy::FindFrUnitSize( |
| const GridSpan& tracks_span, |
| LayoutUnit left_over_space) const { |
| return algorithm_.FindFrUnitSize(tracks_span, left_over_space); |
| } |
| |
| void GridTrackSizingAlgorithmStrategy::DistributeSpaceToTracks( |
| Vector<GridTrack*>& tracks, |
| LayoutUnit& available_logical_space) const { |
| algorithm_.DistributeSpaceToTracks<kMaximizeTracks>(tracks, nullptr, |
| available_logical_space); |
| } |
| |
| Optional<LayoutUnit> |
| GridTrackSizingAlgorithmStrategy::ExtentForBaselineAlignment( |
| LayoutBox& child) const { |
| auto* grid = algorithm_.layout_grid_; |
| GridAxis baseline_axis = GridLayoutUtils::IsOrthogonalChild(*grid, child) |
| ? kGridRowAxis |
| : kGridColumnAxis; |
| if (!grid->IsBaselineAlignmentForChild(child, baseline_axis) || |
| !grid->IsBaselineContextComputed(baseline_axis)) |
| return WTF::nullopt; |
| |
| auto& group = grid->GetBaselineGroupForChild(child, baseline_axis); |
| return group.MaxAscent() + group.MaxDescent(); |
| } |
| |
| LayoutUnit DefiniteSizeStrategy::MinLogicalWidthForChild( |
| LayoutBox& child, |
| Length child_min_size, |
| LayoutUnit available_size) const { |
| return child.ComputeLogicalWidthUsing(kMinSize, child_min_size, |
| available_size, GetLayoutGrid()) + |
| GridLayoutUtils::MarginLogicalWidthForChild(*GetLayoutGrid(), child); |
| } |
| |
| void DefiniteSizeStrategy::LayoutGridItemForMinSizeComputation( |
| LayoutBox& child, |
| bool override_size_has_changed) const { |
| if (override_size_has_changed) |
| SetNeedsLayoutForChild(child); |
| child.LayoutIfNeeded(); |
| } |
| |
| void DefiniteSizeStrategy::MaximizeTracks(Vector<GridTrack>& tracks, |
| Optional<LayoutUnit>& free_space) { |
| size_t tracks_size = tracks.size(); |
| Vector<GridTrack*> tracks_for_distribution(tracks_size); |
| for (size_t i = 0; i < tracks_size; ++i) { |
| tracks_for_distribution[i] = tracks.data() + i; |
| tracks_for_distribution[i]->SetPlannedSize( |
| tracks_for_distribution[i]->BaseSize()); |
| } |
| |
| DCHECK(free_space); |
| DistributeSpaceToTracks(tracks_for_distribution, free_space.value()); |
| |
| for (auto* track : tracks_for_distribution) |
| track->SetBaseSize(track->PlannedSize()); |
| } |
| |
| double DefiniteSizeStrategy::FindUsedFlexFraction( |
| Vector<size_t>& flexible_sized_tracks_index, |
| GridTrackSizingDirection direction, |
| Optional<LayoutUnit> free_space) const { |
| GridSpan all_tracks_span = GridSpan::TranslatedDefiniteGridSpan( |
| 0, algorithm_.Tracks(direction).size()); |
| DCHECK(free_space); |
| return FindFrUnitSize(all_tracks_span, free_space.value()); |
| } |
| |
| LayoutUnit DefiniteSizeStrategy::FreeSpaceForStretchAutoTracksStep() const { |
| DCHECK(algorithm_.FreeSpace(Direction())); |
| return algorithm_.FreeSpace(Direction()).value(); |
| } |
| |
| LayoutUnit IndefiniteSizeStrategy::MinLogicalWidthForChild( |
| LayoutBox& child, |
| Length child_min_size, |
| LayoutUnit available_size) const { |
| // TODO(svillar): we should use marginIntrinsicLogicalWidthForChild() instead |
| // but it is protected for LayoutObjects. Apparently none of the current tests |
| // fail, so we need a test case for this too. |
| LayoutUnit margin_logical_width = LayoutUnit(); |
| return child.ComputeLogicalWidthUsing(kMinSize, child_min_size, |
| available_size, GetLayoutGrid()) + |
| margin_logical_width; |
| } |
| |
| void IndefiniteSizeStrategy::LayoutGridItemForMinSizeComputation( |
| LayoutBox& child, |
| bool override_size_has_changed) const { |
| if (override_size_has_changed && Direction() != kForColumns) |
| SetNeedsLayoutForChild(child); |
| child.LayoutIfNeeded(); |
| } |
| |
| void IndefiniteSizeStrategy::MaximizeTracks(Vector<GridTrack>& tracks, |
| Optional<LayoutUnit>&) { |
| for (auto& track : tracks) |
| track.SetBaseSize(track.GrowthLimit()); |
| } |
| |
| static inline double NormalizedFlexFraction(const GridTrack& track, |
| double flex_factor) { |
| return track.BaseSize() / std::max<double>(1, flex_factor); |
| } |
| |
| double IndefiniteSizeStrategy::FindUsedFlexFraction( |
| Vector<size_t>& flexible_sized_tracks_index, |
| GridTrackSizingDirection direction, |
| Optional<LayoutUnit>) const { |
| auto all_tracks = algorithm_.Tracks(direction); |
| |
| double flex_fraction = 0; |
| for (const auto& track_index : flexible_sized_tracks_index) { |
| // TODO(svillar): we pass TrackSizing to gridTrackSize() because it does not |
| // really matter as we know the track is a flex sized track. It'd be nice |
| // not to have to do that. |
| flex_fraction = std::max( |
| flex_fraction, NormalizedFlexFraction( |
| all_tracks[track_index], |
| algorithm_.GetGridTrackSize(direction, track_index) |
| .MaxTrackBreadth() |
| .Flex())); |
| } |
| |
| const Grid& grid = algorithm_.GetGrid(); |
| if (!grid.HasGridItems()) |
| return flex_fraction; |
| |
| for (size_t i = 0; i < flexible_sized_tracks_index.size(); ++i) { |
| GridIterator iterator(grid, direction, flexible_sized_tracks_index[i]); |
| while (LayoutBox* grid_item = iterator.NextGridItem()) { |
| const GridSpan& span = grid.GridItemSpan(*grid_item, direction); |
| |
| // Do not include already processed items. |
| if (i > 0 && span.StartLine() <= flexible_sized_tracks_index[i - 1]) |
| continue; |
| |
| flex_fraction = std::max( |
| flex_fraction, FindFrUnitSize(span, MaxContentForChild(*grid_item))); |
| } |
| } |
| |
| return flex_fraction; |
| } |
| |
| bool IndefiniteSizeStrategy::RecomputeUsedFlexFractionIfNeeded( |
| Vector<size_t>& flexible_sized_tracks_index, |
| double& flex_fraction, |
| Vector<LayoutUnit>& increments, |
| LayoutUnit& total_growth) const { |
| if (Direction() == kForColumns) |
| return false; |
| |
| const LayoutGrid* layout_grid = this->GetLayoutGrid(); |
| LayoutUnit min_size = layout_grid->ComputeContentLogicalHeight( |
| kMinSize, layout_grid->StyleRef().LogicalMinHeight(), LayoutUnit(-1)); |
| LayoutUnit max_size = layout_grid->ComputeContentLogicalHeight( |
| kMaxSize, layout_grid->StyleRef().LogicalMaxHeight(), LayoutUnit(-1)); |
| |
| // Redo the flex fraction computation using min|max-height as definite |
| // available space in case the total height is smaller than min-height or |
| // larger than max-height. |
| LayoutUnit rows_size = total_growth + ComputeTrackBasedSize(); |
| bool check_min_size = min_size && rows_size < min_size; |
| bool check_max_size = max_size != -1 && rows_size > max_size; |
| if (!check_min_size && !check_max_size) |
| return false; |
| |
| LayoutUnit free_space = check_max_size ? max_size : LayoutUnit(-1); |
| const Grid& grid = algorithm_.GetGrid(); |
| free_space = |
| std::max(free_space, min_size) - |
| layout_grid->GuttersSize(grid, kForRows, 0, grid.NumTracks(kForRows), |
| AvailableSpace()); |
| |
| size_t number_of_tracks = algorithm_.Tracks(Direction()).size(); |
| flex_fraction = FindFrUnitSize( |
| GridSpan::TranslatedDefiniteGridSpan(0, number_of_tracks), free_space); |
| return true; |
| } |
| |
| LayoutUnit IndefiniteSizeStrategy::FreeSpaceForStretchAutoTracksStep() const { |
| DCHECK(!algorithm_.FreeSpace(Direction())); |
| LayoutUnit min_size; |
| |
| if (Direction() == kForColumns) { |
| // TODO(rego): Review intrinsic sizes in follow-up patches. For flexible |
| // tracks we will need to do something similar if we want to have accurate |
| // intrinsic sizes. Even in ComputeGridContainerIntrinsicSizes() we should |
| // consider this. |
| Length min_width = GetLayoutGrid()->StyleRef().LogicalMinWidth(); |
| if (min_width.IsFixed() && min_width.Value() > 0) { |
| min_size = GetLayoutGrid()->AdjustContentBoxLogicalWidthForBoxSizing( |
| LayoutUnit(min_width.Value())); |
| } else { |
| return LayoutUnit(); |
| } |
| } else { |
| min_size = GetLayoutGrid()->ComputeContentLogicalHeight( |
| kMinSize, GetLayoutGrid()->StyleRef().LogicalMinHeight(), |
| LayoutUnit(-1)); |
| } |
| return min_size - ComputeTrackBasedSize(); |
| } |
| |
| Optional<LayoutUnit> GridTrackSizingAlgorithm::FreeSpace( |
| GridTrackSizingDirection direction) const { |
| return direction == kForRows ? free_space_rows_ : free_space_columns_; |
| } |
| |
| Optional<LayoutUnit> GridTrackSizingAlgorithm::AvailableSpace( |
| GridTrackSizingDirection direction) const { |
| return direction == kForRows ? available_space_rows_ |
| : available_space_columns_; |
| } |
| |
| Optional<LayoutUnit> GridTrackSizingAlgorithm::AvailableSpace() const { |
| return AvailableSpace(direction_); |
| } |
| |
| void GridTrackSizingAlgorithm::SetAvailableSpace( |
| GridTrackSizingDirection direction, |
| Optional<LayoutUnit> available_space) { |
| if (direction == kForColumns) |
| available_space_columns_ = available_space; |
| else |
| available_space_rows_ = available_space; |
| } |
| |
| Vector<GridTrack>& GridTrackSizingAlgorithm::Tracks( |
| GridTrackSizingDirection direction) { |
| return direction == kForColumns ? columns_ : rows_; |
| } |
| |
| const Vector<GridTrack>& GridTrackSizingAlgorithm::Tracks( |
| GridTrackSizingDirection direction) const { |
| return direction == kForColumns ? columns_ : rows_; |
| } |
| |
| void GridTrackSizingAlgorithm::SetFreeSpace(GridTrackSizingDirection direction, |
| Optional<LayoutUnit> free_space) { |
| if (direction == kForColumns) |
| free_space_columns_ = free_space; |
| else |
| free_space_rows_ = free_space; |
| } |
| |
| GridTrackSize GridTrackSizingAlgorithm::RawGridTrackSize( |
| GridTrackSizingDirection direction, |
| size_t translated_index) const { |
| bool is_row_axis = direction == kForColumns; |
| const Vector<GridTrackSize>& track_styles = |
| is_row_axis ? layout_grid_->StyleRef().GridTemplateColumns() |
| : layout_grid_->StyleRef().GridTemplateRows(); |
| const Vector<GridTrackSize>& auto_repeat_track_styles = |
| is_row_axis ? layout_grid_->StyleRef().GridAutoRepeatColumns() |
| : layout_grid_->StyleRef().GridAutoRepeatRows(); |
| const Vector<GridTrackSize>& auto_track_styles = |
| is_row_axis ? layout_grid_->StyleRef().GridAutoColumns() |
| : layout_grid_->StyleRef().GridAutoRows(); |
| size_t insertion_point = |
| is_row_axis |
| ? layout_grid_->StyleRef().GridAutoRepeatColumnsInsertionPoint() |
| : layout_grid_->StyleRef().GridAutoRepeatRowsInsertionPoint(); |
| size_t auto_repeat_tracks_count = grid_.AutoRepeatTracks(direction); |
| |
| // We should not use GridPositionsResolver::explicitGridXXXCount() for this |
| // because the explicit grid might be larger than the number of tracks in |
| // grid-template-rows|columns (if grid-template-areas is specified for |
| // example). |
| size_t explicit_tracks_count = track_styles.size() + auto_repeat_tracks_count; |
| |
| int untranslated_index_as_int = |
| translated_index + grid_.SmallestTrackStart(direction); |
| size_t auto_track_styles_size = auto_track_styles.size(); |
| if (untranslated_index_as_int < 0) { |
| int index = |
| untranslated_index_as_int % static_cast<int>(auto_track_styles_size); |
| // We need to traspose the index because the first negative implicit line |
| // will get the last defined auto track and so on. |
| index += index ? auto_track_styles_size : 0; |
| return auto_track_styles[index]; |
| } |
| |
| size_t untranslated_index = static_cast<size_t>(untranslated_index_as_int); |
| if (untranslated_index >= explicit_tracks_count) { |
| return auto_track_styles[(untranslated_index - explicit_tracks_count) % |
| auto_track_styles_size]; |
| } |
| |
| if (LIKELY(!auto_repeat_tracks_count) || untranslated_index < insertion_point) |
| return track_styles[untranslated_index]; |
| |
| if (untranslated_index < (insertion_point + auto_repeat_tracks_count)) { |
| size_t auto_repeat_local_index = |
| untranslated_index_as_int - insertion_point; |
| return auto_repeat_track_styles[auto_repeat_local_index % |
| auto_repeat_track_styles.size()]; |
| } |
| |
| return track_styles[untranslated_index - auto_repeat_tracks_count]; |
| } |
| |
| GridTrackSize GridTrackSizingAlgorithm::GetGridTrackSize( |
| GridTrackSizingDirection direction, |
| size_t translated_index) const { |
| // Collapse empty auto repeat tracks if auto-fit. |
| if (grid_.HasAutoRepeatEmptyTracks(direction) && |
| grid_.IsEmptyAutoRepeatTrack(direction, translated_index)) |
| return {Length(kFixed), kLengthTrackSizing}; |
| |
| const GridTrackSize& track_size = |
| RawGridTrackSize(direction, translated_index); |
| if (track_size.IsFitContent()) |
| return track_size; |
| |
| GridLength min_track_breadth = track_size.MinTrackBreadth(); |
| GridLength max_track_breadth = track_size.MaxTrackBreadth(); |
| // If the logical width/height of the grid container is indefinite, percentage |
| // values are treated as <auto>. |
| if (min_track_breadth.HasPercentage() || max_track_breadth.HasPercentage()) { |
| // TODO(svillar): we should remove the second check later. We need it |
| // because during the second iteration of the algorithm we set definite |
| // sizes in the grid container so percents would not resolve properly (it |
| // would think that the height is definite when it is not). |
| if (!AvailableSpace(direction) || |
| (direction == kForRows && |
| !layout_grid_->CachedHasDefiniteLogicalHeight())) { |
| if (min_track_breadth.HasPercentage()) |
| min_track_breadth = Length(kAuto); |
| if (max_track_breadth.HasPercentage()) |
| max_track_breadth = Length(kAuto); |
| } |
| } |
| |
| // Flex sizes are invalid as a min sizing function. However we still can have |
| // a flexible |minTrackBreadth| if the track had a flex size directly (e.g. |
| // "1fr"), the spec says that in this case it implies an automatic minimum. |
| if (min_track_breadth.IsFlex()) |
| min_track_breadth = Length(kAuto); |
| |
| return GridTrackSize(min_track_breadth, max_track_breadth); |
| } |
| |
| LayoutUnit GridTrackSizingAlgorithm::InitialBaseSize( |
| const GridTrackSize& track_size) const { |
| const GridLength& grid_length = track_size.MinTrackBreadth(); |
| if (grid_length.IsFlex()) |
| return LayoutUnit(); |
| |
| const Length& track_length = grid_length.length(); |
| if (track_length.IsSpecified()) { |
| return ValueForLength(track_length, |
| AvailableSpace().value_or(LayoutUnit())); |
| } |
| |
| DCHECK(track_length.IsMinContent() || track_length.IsAuto() || |
| track_length.IsMaxContent()); |
| return LayoutUnit(); |
| } |
| |
| LayoutUnit GridTrackSizingAlgorithm::InitialGrowthLimit( |
| const GridTrackSize& track_size, |
| LayoutUnit base_size) const { |
| const GridLength& grid_length = track_size.MaxTrackBreadth(); |
| if (grid_length.IsFlex()) |
| return base_size; |
| |
| const Length& track_length = grid_length.length(); |
| if (track_length.IsSpecified()) { |
| return ValueForLength(track_length, |
| AvailableSpace().value_or(LayoutUnit())); |
| } |
| |
| DCHECK(track_length.IsMinContent() || track_length.IsAuto() || |
| track_length.IsMaxContent()); |
| return LayoutUnit(kInfinity); |
| } |
| |
| void GridTrackSizingAlgorithm::InitializeTrackSizes() { |
| DCHECK(content_sized_tracks_index_.IsEmpty()); |
| DCHECK(flexible_sized_tracks_index_.IsEmpty()); |
| DCHECK(auto_sized_tracks_for_stretch_index_.IsEmpty()); |
| Vector<GridTrack>& track_list = Tracks(direction_); |
| bool has_definite_free_space = !!AvailableSpace(); |
| size_t num_tracks = track_list.size(); |
| for (size_t i = 0; i < num_tracks; ++i) { |
| GridTrackSize track_size = GetGridTrackSize(direction_, i); |
| GridTrack& track = track_list[i]; |
| track.SetBaseSize(InitialBaseSize(track_size)); |
| track.SetGrowthLimit(InitialGrowthLimit(track_size, track.BaseSize())); |
| track.SetInfinitelyGrowable(false); |
| |
| if (track_size.IsFitContent()) { |
| GridLength grid_length = track_size.FitContentTrackBreadth(); |
| if (!grid_length.HasPercentage() || has_definite_free_space) { |
| track.SetGrowthLimitCap(ValueForLength( |
| grid_length.length(), AvailableSpace().value_or(LayoutUnit()))); |
| } |
| } |
| |
| if (track_size.IsContentSized()) |
| content_sized_tracks_index_.push_back(i); |
| if (track_size.MaxTrackBreadth().IsFlex()) |
| flexible_sized_tracks_index_.push_back(i); |
| if (track_size.HasAutoMaxTrackBreadth() && !track_size.IsFitContent()) |
| auto_sized_tracks_for_stretch_index_.push_back(i); |
| } |
| } |
| |
| void GridTrackSizingAlgorithm::SizeTrackToFitNonSpanningItem( |
| const GridSpan& span, |
| LayoutBox& grid_item, |
| GridTrack& track) { |
| const size_t track_position = span.StartLine(); |
| GridTrackSize track_size = GetGridTrackSize(direction_, track_position); |
| |
| if (track_size.HasMinContentMinTrackBreadth()) { |
| track.SetBaseSize( |
| std::max(track.BaseSize(), strategy_->MinContentForChild(grid_item))); |
| } else if (track_size.HasMaxContentMinTrackBreadth()) { |
| track.SetBaseSize( |
| std::max(track.BaseSize(), strategy_->MaxContentForChild(grid_item))); |
| } else if (track_size.HasAutoMinTrackBreadth()) { |
| track.SetBaseSize( |
| std::max(track.BaseSize(), strategy_->MinSizeForChild(grid_item))); |
| } |
| |
| if (track_size.HasMinContentMaxTrackBreadth()) { |
| track.SetGrowthLimit(std::max(track.GrowthLimit(), |
| strategy_->MinContentForChild(grid_item))); |
| } else if (track_size.HasMaxContentOrAutoMaxTrackBreadth()) { |
| LayoutUnit growth_limit = strategy_->MaxContentForChild(grid_item); |
| if (track_size.IsFitContent()) { |
| growth_limit = |
| std::min(growth_limit, |
| ValueForLength(track_size.FitContentTrackBreadth().length(), |
| AvailableSpace().value_or(LayoutUnit()))); |
| } |
| track.SetGrowthLimit(std::max(track.GrowthLimit(), growth_limit)); |
| } |
| } |
| |
| bool GridTrackSizingAlgorithm::SpanningItemCrossesFlexibleSizedTracks( |
| const GridSpan& span) const { |
| for (const auto& track_position : span) { |
| const GridTrackSize& track_size = |
| GetGridTrackSize(direction_, track_position); |
| if (track_size.MinTrackBreadth().IsFlex() || |
| track_size.MaxTrackBreadth().IsFlex()) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // We're basically using a class instead of a std::pair because of accessing |
| // gridItem() or getGridSpan() is much more self-explanatory that using .first |
| // or .second members in the pair. Having a std::pair<LayoutBox*, size_t> |
| // does not work either because we still need the GridSpan so we'd have to add |
| // an extra hash lookup for each item. |
| class GridItemWithSpan { |
| public: |
| GridItemWithSpan(LayoutBox& grid_item, const GridSpan& grid_span) |
| : grid_item_(&grid_item), grid_span_(grid_span) {} |
| |
| LayoutBox& GridItem() const { return *grid_item_; } |
| GridSpan GetGridSpan() const { return grid_span_; } |
| |
| bool operator<(const GridItemWithSpan other) const { |
| return grid_span_.IntegerSpan() < other.grid_span_.IntegerSpan(); |
| } |
| |
| private: |
| LayoutBox* grid_item_; |
| GridSpan grid_span_; |
| }; |
| |
| struct GridItemsSpanGroupRange { |
| Vector<GridItemWithSpan>::iterator range_start; |
| Vector<GridItemWithSpan>::iterator range_end; |
| }; |
| |
| enum TrackSizeRestriction { |
| kAllowInfinity, |
| kForbidInfinity, |
| }; |
| |
| static LayoutUnit TrackSizeForTrackSizeComputationPhase( |
| TrackSizeComputationPhase phase, |
| const GridTrack& track, |
| TrackSizeRestriction restriction) { |
| switch (phase) { |
| case kResolveIntrinsicMinimums: |
| case kResolveContentBasedMinimums: |
| case kResolveMaxContentMinimums: |
| case kMaximizeTracks: |
| return track.BaseSize(); |
| case kResolveIntrinsicMaximums: |
| case kResolveMaxContentMaximums: |
| const LayoutUnit& growth_limit = track.GrowthLimit(); |
| if (restriction == kAllowInfinity) |
| return growth_limit; |
| return growth_limit == kInfinity ? track.BaseSize() : growth_limit; |
| } |
| |
| NOTREACHED(); |
| return track.BaseSize(); |
| } |
| |
| static bool ShouldProcessTrackForTrackSizeComputationPhase( |
| TrackSizeComputationPhase phase, |
| const GridTrackSize& track_size) { |
| switch (phase) { |
| case kResolveIntrinsicMinimums: |
| return track_size.HasIntrinsicMinTrackBreadth(); |
| case kResolveContentBasedMinimums: |
| return track_size.HasMinOrMaxContentMinTrackBreadth(); |
| case kResolveMaxContentMinimums: |
| return track_size.HasMaxContentMinTrackBreadth(); |
| case kResolveIntrinsicMaximums: |
| return track_size.HasIntrinsicMaxTrackBreadth(); |
| case kResolveMaxContentMaximums: |
| return track_size.HasMaxContentOrAutoMaxTrackBreadth(); |
| case kMaximizeTracks: |
| NOTREACHED(); |
| return false; |
| } |
| |
| NOTREACHED(); |
| return false; |
| } |
| |
| static bool TrackShouldGrowBeyondGrowthLimitsForTrackSizeComputationPhase( |
| TrackSizeComputationPhase phase, |
| const GridTrackSize& track_size) { |
| switch (phase) { |
| case kResolveIntrinsicMinimums: |
| case kResolveContentBasedMinimums: |
| return track_size |
| .HasAutoOrMinContentMinTrackBreadthAndIntrinsicMaxTrackBreadth(); |
| case kResolveMaxContentMinimums: |
| return track_size |
| .HasMaxContentMinTrackBreadthAndMaxContentMaxTrackBreadth(); |
| case kResolveIntrinsicMaximums: |
| case kResolveMaxContentMaximums: |
| return true; |
| case kMaximizeTracks: |
| NOTREACHED(); |
| return false; |
| } |
| |
| NOTREACHED(); |
| return false; |
| } |
| |
| static void MarkAsInfinitelyGrowableForTrackSizeComputationPhase( |
| TrackSizeComputationPhase phase, |
| GridTrack& track) { |
| switch (phase) { |
| case kResolveIntrinsicMinimums: |
| case kResolveContentBasedMinimums: |
| case kResolveMaxContentMinimums: |
| return; |
| case kResolveIntrinsicMaximums: |
| if (TrackSizeForTrackSizeComputationPhase(phase, track, kAllowInfinity) == |
| kInfinity && |
| track.PlannedSize() != kInfinity) |
| track.SetInfinitelyGrowable(true); |
| return; |
| case kResolveMaxContentMaximums: |
| if (track.InfinitelyGrowable()) |
| track.SetInfinitelyGrowable(false); |
| return; |
| case kMaximizeTracks: |
| NOTREACHED(); |
| return; |
| } |
| |
| NOTREACHED(); |
| } |
| |
| static void UpdateTrackSizeForTrackSizeComputationPhase( |
| TrackSizeComputationPhase phase, |
| GridTrack& track) { |
| switch (phase) { |
| case kResolveIntrinsicMinimums: |
| case kResolveContentBasedMinimums: |
| case kResolveMaxContentMinimums: |
| track.SetBaseSize(track.PlannedSize()); |
| return; |
| case kResolveIntrinsicMaximums: |
| case kResolveMaxContentMaximums: |
| track.SetGrowthLimit(track.PlannedSize()); |
| return; |
| case kMaximizeTracks: |
| NOTREACHED(); |
| return; |
| } |
| |
| NOTREACHED(); |
| } |
| |
| LayoutUnit GridTrackSizingAlgorithm::ItemSizeForTrackSizeComputationPhase( |
| TrackSizeComputationPhase phase, |
| LayoutBox& grid_item) const { |
| switch (phase) { |
| case kResolveIntrinsicMinimums: |
| case kResolveIntrinsicMaximums: |
| return strategy_->MinSizeForChild(grid_item); |
| case kResolveContentBasedMinimums: |
| return strategy_->MinContentForChild(grid_item); |
| case kResolveMaxContentMinimums: |
| case kResolveMaxContentMaximums: |
| return strategy_->MaxContentForChild(grid_item); |
| case kMaximizeTracks: |
| NOTREACHED(); |
| return LayoutUnit(); |
| } |
| |
| NOTREACHED(); |
| return LayoutUnit(); |
| } |
| |
| static bool SortByGridTrackGrowthPotential(const GridTrack* track1, |
| const GridTrack* track2) { |
| // This check ensures that we respect the irreflexivity property of the strict |
| // weak ordering required by std::sort(forall x: NOT x < x). |
| bool track1_has_infinite_growth_potential_without_cap = |
| track1->InfiniteGrowthPotential() && !track1->GrowthLimitCap(); |
| bool track2_has_infinite_growth_potential_without_cap = |
| track2->InfiniteGrowthPotential() && !track2->GrowthLimitCap(); |
| |
| if (track1_has_infinite_growth_potential_without_cap && |
| track2_has_infinite_growth_potential_without_cap) |
| return false; |
| |
| if (track1_has_infinite_growth_potential_without_cap || |
| track2_has_infinite_growth_potential_without_cap) |
| return track2_has_infinite_growth_potential_without_cap; |
| |
| LayoutUnit track1_limit = |
| track1->GrowthLimitCap().value_or(track1->GrowthLimit()); |
| LayoutUnit track2_limit = |
| track2->GrowthLimitCap().value_or(track2->GrowthLimit()); |
| return (track1_limit - track1->BaseSize()) < |
| (track2_limit - track2->BaseSize()); |
| } |
| |
| static void ClampGrowthShareIfNeeded(TrackSizeComputationPhase phase, |
| const GridTrack& track, |
| LayoutUnit& growth_share) { |
| if (phase != kResolveMaxContentMaximums || !track.GrowthLimitCap()) |
| return; |
| |
| LayoutUnit distance_to_cap = |
| track.GrowthLimitCap().value() - track.SizeDuringDistribution(); |
| if (distance_to_cap <= 0) |
| return; |
| |
| growth_share = std::min(growth_share, distance_to_cap); |
| } |
| |
| template <TrackSizeComputationPhase phase> |
| void GridTrackSizingAlgorithm::DistributeSpaceToTracks( |
| Vector<GridTrack*>& tracks, |
| Vector<GridTrack*>* grow_beyond_growth_limits_tracks, |
| LayoutUnit& available_logical_space) const { |
| DCHECK_GE(available_logical_space, 0); |
| |
| for (auto* track : tracks) { |
| track->SetSizeDuringDistribution( |
| TrackSizeForTrackSizeComputationPhase(phase, *track, kForbidInfinity)); |
| } |
| |
| if (available_logical_space > 0) { |
| std::sort(tracks.begin(), tracks.end(), SortByGridTrackGrowthPotential); |
| |
| size_t tracks_size = tracks.size(); |
| for (size_t i = 0; i < tracks_size; ++i) { |
| GridTrack& track = *tracks[i]; |
| LayoutUnit available_logical_space_share = |
| available_logical_space / (tracks_size - i); |
| const LayoutUnit& track_breadth = |
| TrackSizeForTrackSizeComputationPhase(phase, track, kForbidInfinity); |
| LayoutUnit growth_share = |
| track.InfiniteGrowthPotential() |
| ? available_logical_space_share |
| : std::min(available_logical_space_share, |
| track.GrowthLimit() - track_breadth); |
| ClampGrowthShareIfNeeded(phase, track, growth_share); |
| DCHECK_GE(growth_share, 0) << "We must never shrink any grid track or " |
| "else we can't guarantee we abide by our " |
| "min-sizing function."; |
| track.GrowSizeDuringDistribution(growth_share); |
| available_logical_space -= growth_share; |
| } |
| } |
| |
| if (available_logical_space > 0 && grow_beyond_growth_limits_tracks) { |
| // We need to sort them because there might be tracks with growth limit caps |
| // (like the ones with fit-content()) which cannot indefinitely grow over |
| // the limits. |
| if (phase == kResolveMaxContentMaximums) { |
| std::sort(grow_beyond_growth_limits_tracks->begin(), |
| grow_beyond_growth_limits_tracks->end(), |
| SortByGridTrackGrowthPotential); |
| } |
| |
| size_t tracks_growing_above_max_breadth_size = |
| grow_beyond_growth_limits_tracks->size(); |
| for (size_t i = 0; i < tracks_growing_above_max_breadth_size; ++i) { |
| GridTrack* track = grow_beyond_growth_limits_tracks->at(i); |
| LayoutUnit growth_share = |
| available_logical_space / (tracks_growing_above_max_breadth_size - i); |
| ClampGrowthShareIfNeeded(phase, *track, growth_share); |
| DCHECK_GE(growth_share, 0) << "We must never shrink any grid track or " |
| "else we can't guarantee we abide by our " |
| "min-sizing function."; |
| track->GrowSizeDuringDistribution(growth_share); |
| available_logical_space -= growth_share; |
| } |
| } |
| |
| for (auto* track : tracks) { |
| track->SetPlannedSize( |
| track->PlannedSize() == kInfinity |
| ? track->SizeDuringDistribution() |
| : std::max(track->PlannedSize(), track->SizeDuringDistribution())); |
| } |
| } |
| |
| template <TrackSizeComputationPhase phase> |
| void GridTrackSizingAlgorithm::IncreaseSizesToAccommodateSpanningItems( |
| const GridItemsSpanGroupRange& grid_items_with_span) { |
| Vector<GridTrack>& all_tracks = Tracks(direction_); |
| for (const auto& track_index : content_sized_tracks_index_) { |
| GridTrack& track = all_tracks[track_index]; |
| track.SetPlannedSize( |
| TrackSizeForTrackSizeComputationPhase(phase, track, kAllowInfinity)); |
| } |
| |
| Vector<GridTrack*> grow_beyond_growth_limits_tracks; |
| Vector<GridTrack*> filtered_tracks; |
| for (auto* it = grid_items_with_span.range_start; |
| it != grid_items_with_span.range_end; ++it) { |
| GridItemWithSpan& grid_item_with_span = *it; |
| DCHECK_GT(grid_item_with_span.GetGridSpan().IntegerSpan(), 1u); |
| const GridSpan& item_span = grid_item_with_span.GetGridSpan(); |
| |
| grow_beyond_growth_limits_tracks.Shrink(0); |
| filtered_tracks.Shrink(0); |
| LayoutUnit spanning_tracks_size; |
| for (const auto& track_position : item_span) { |
| GridTrackSize track_size = GetGridTrackSize(direction_, track_position); |
| GridTrack& track = Tracks(direction_)[track_position]; |
| spanning_tracks_size += |
| TrackSizeForTrackSizeComputationPhase(phase, track, kForbidInfinity); |
| if (!ShouldProcessTrackForTrackSizeComputationPhase(phase, track_size)) |
| continue; |
| |
| filtered_tracks.push_back(&track); |
| |
| if (TrackShouldGrowBeyondGrowthLimitsForTrackSizeComputationPhase( |
| phase, track_size)) |
| grow_beyond_growth_limits_tracks.push_back(&track); |
| } |
| |
| if (filtered_tracks.IsEmpty()) |
| continue; |
| |
| spanning_tracks_size += |
| layout_grid_->GuttersSize(grid_, direction_, item_span.StartLine(), |
| item_span.IntegerSpan(), AvailableSpace()); |
| |
| LayoutUnit extra_space = ItemSizeForTrackSizeComputationPhase( |
| phase, grid_item_with_span.GridItem()) - |
| spanning_tracks_size; |
| extra_space = extra_space.ClampNegativeToZero(); |
| auto& tracks_to_grow_beyond_growth_limits = |
| grow_beyond_growth_limits_tracks.IsEmpty() |
| ? filtered_tracks |
| : grow_beyond_growth_limits_tracks; |
| DistributeSpaceToTracks<phase>( |
| filtered_tracks, &tracks_to_grow_beyond_growth_limits, extra_space); |
| } |
| |
| for (const auto& track_index : content_sized_tracks_index_) { |
| GridTrack& track = all_tracks[track_index]; |
| MarkAsInfinitelyGrowableForTrackSizeComputationPhase(phase, track); |
| UpdateTrackSizeForTrackSizeComputationPhase(phase, track); |
| } |
| } |
| |
| void GridTrackSizingAlgorithm::ResolveIntrinsicTrackSizes() { |
| Vector<GridItemWithSpan> items_sorted_by_increasing_span; |
| if (grid_.HasGridItems()) { |
| HashSet<LayoutBox*> items_set; |
| for (const auto& track_index : content_sized_tracks_index_) { |
| GridIterator iterator(grid_, direction_, track_index); |
| GridTrack& track = Tracks(direction_)[track_index]; |
| while (LayoutBox* grid_item = iterator.NextGridItem()) { |
| if (items_set.insert(grid_item).is_new_entry) { |
| const GridSpan& span = grid_.GridItemSpan(*grid_item, direction_); |
| if (span.IntegerSpan() == 1) { |
| SizeTrackToFitNonSpanningItem(span, *grid_item, track); |
| } else if (!SpanningItemCrossesFlexibleSizedTracks(span)) { |
| items_sorted_by_increasing_span.push_back( |
| GridItemWithSpan(*grid_item, span)); |
| } |
| } |
| } |
| } |
| std::sort(items_sorted_by_increasing_span.begin(), |
| items_sorted_by_increasing_span.end()); |
| } |
| |
| auto* it = items_sorted_by_increasing_span.begin(); |
| auto* end = items_sorted_by_increasing_span.end(); |
| while (it != end) { |
| GridItemsSpanGroupRange span_group_range = {it, |
| std::upper_bound(it, end, *it)}; |
| IncreaseSizesToAccommodateSpanningItems<kResolveIntrinsicMinimums>( |
| span_group_range); |
| IncreaseSizesToAccommodateSpanningItems<kResolveContentBasedMinimums>( |
| span_group_range); |
| IncreaseSizesToAccommodateSpanningItems<kResolveMaxContentMinimums>( |
| span_group_range); |
| IncreaseSizesToAccommodateSpanningItems<kResolveIntrinsicMaximums>( |
| span_group_range); |
| IncreaseSizesToAccommodateSpanningItems<kResolveMaxContentMaximums>( |
| span_group_range); |
| it = span_group_range.range_end; |
| } |
| |
| for (const auto& track_index : content_sized_tracks_index_) { |
| GridTrack& track = Tracks(direction_)[track_index]; |
| if (track.GrowthLimit() == kInfinity) |
| track.SetGrowthLimit(track.BaseSize()); |
| } |
| } |
| |
| void GridTrackSizingAlgorithm::ComputeGridContainerIntrinsicSizes() { |
| min_content_size_ = max_content_size_ = LayoutUnit(); |
| |
| Vector<GridTrack>& all_tracks = Tracks(direction_); |
| for (auto& track : all_tracks) { |
| DCHECK(!track.InfiniteGrowthPotential()); |
| min_content_size_ += track.BaseSize(); |
| max_content_size_ += track.GrowthLimit(); |
| // The growth limit caps must be cleared now in order to properly sort |
| // tracks by growth potential on an eventual "Maximize Tracks". |
| track.SetGrowthLimitCap(WTF::nullopt); |
| } |
| } |
| |
| LayoutUnit GridTrackSizingAlgorithm::ComputeTrackBasedSize() const { |
| LayoutUnit size; |
| |
| const Vector<GridTrack>& all_tracks = Tracks(direction_); |
| for (auto& track : all_tracks) |
| size += track.BaseSize(); |
| |
| size += layout_grid_->GuttersSize(grid_, direction_, 0, all_tracks.size(), |
| AvailableSpace()); |
| |
| return size; |
| } |
| |
| double GridTrackSizingAlgorithm::FindFrUnitSize( |
| const GridSpan& tracks_span, |
| LayoutUnit left_over_space) const { |
| if (left_over_space <= 0) |
| return 0; |
| |
| const Vector<GridTrack>& all_tracks = Tracks(direction_); |
| double flex_factor_sum = 0; |
| Vector<size_t, 8> flexible_tracks_indexes; |
| for (const auto& track_index : tracks_span) { |
| GridTrackSize track_size = GetGridTrackSize(direction_, track_index); |
| if (!track_size.MaxTrackBreadth().IsFlex()) { |
| left_over_space -= all_tracks[track_index].BaseSize(); |
| } else { |
| flexible_tracks_indexes.push_back(track_index); |
| flex_factor_sum += track_size.MaxTrackBreadth().Flex(); |
| } |
| } |
| |
| // The function is not called if we don't have <flex> grid tracks. |
| DCHECK(!flexible_tracks_indexes.IsEmpty()); |
| |
| return ComputeFlexFactorUnitSize(all_tracks, flex_factor_sum, left_over_space, |
| flexible_tracks_indexes); |
| } |
| |
| double GridTrackSizingAlgorithm::ComputeFlexFactorUnitSize( |
| const Vector<GridTrack>& tracks, |
| double flex_factor_sum, |
| LayoutUnit& left_over_space, |
| const Vector<size_t, 8>& flexible_tracks_indexes, |
| std::unique_ptr<TrackIndexSet> tracks_to_treat_as_inflexible) const { |
| // We want to avoid the effect of flex factors sum below 1 making the factor |
| // unit size to grow exponentially. |
| double hypothetical_factor_unit_size = |
| left_over_space / std::max<double>(1, flex_factor_sum); |
| |
| // product of the hypothetical "flex factor unit" and any flexible track's |
| // "flex factor" must be grater than such track's "base size". |
| std::unique_ptr<TrackIndexSet> additional_tracks_to_treat_as_inflexible = |
| std::move(tracks_to_treat_as_inflexible); |
| bool valid_flex_factor_unit = true; |
| for (auto index : flexible_tracks_indexes) { |
| if (additional_tracks_to_treat_as_inflexible && |
| additional_tracks_to_treat_as_inflexible->Contains(index)) |
| continue; |
| LayoutUnit base_size = tracks[index].BaseSize(); |
| double flex_factor = |
| GetGridTrackSize(direction_, index).MaxTrackBreadth().Flex(); |
| // treating all such tracks as inflexible. |
| if (base_size > hypothetical_factor_unit_size * flex_factor) { |
| left_over_space -= base_size; |
| flex_factor_sum -= flex_factor; |
| if (!additional_tracks_to_treat_as_inflexible) |
| additional_tracks_to_treat_as_inflexible = |
| WTF::MakeUnique<TrackIndexSet>(); |
| additional_tracks_to_treat_as_inflexible->insert(index); |
| valid_flex_factor_unit = false; |
| } |
| } |
| if (!valid_flex_factor_unit) { |
| return ComputeFlexFactorUnitSize( |
| tracks, flex_factor_sum, left_over_space, flexible_tracks_indexes, |
| std::move(additional_tracks_to_treat_as_inflexible)); |
| } |
| return hypothetical_factor_unit_size; |
| } |
| |
| void GridTrackSizingAlgorithm::ComputeFlexSizedTracksGrowth( |
| double flex_fraction, |
| Vector<LayoutUnit>& increments, |
| LayoutUnit& total_growth) const { |
| size_t num_flex_tracks = flexible_sized_tracks_index_.size(); |
| DCHECK_EQ(increments.size(), num_flex_tracks); |
| const Vector<GridTrack>& all_tracks = Tracks(direction_); |
| for (size_t i = 0; i < num_flex_tracks; ++i) { |
| size_t track_index = flexible_sized_tracks_index_[i]; |
| auto track_size = GetGridTrackSize(direction_, track_index); |
| DCHECK(track_size.MaxTrackBreadth().IsFlex()); |
| LayoutUnit old_base_size = all_tracks[track_index].BaseSize(); |
| LayoutUnit new_base_size = std::max( |
| old_base_size, |
| LayoutUnit(flex_fraction * track_size.MaxTrackBreadth().Flex())); |
| increments[i] = new_base_size - old_base_size; |
| total_growth += increments[i]; |
| } |
| } |
| |
| void GridTrackSizingAlgorithm::StretchFlexibleTracks( |
| Optional<LayoutUnit> free_space) { |
| if (flexible_sized_tracks_index_.IsEmpty()) |
| return; |
| |
| double flex_fraction = strategy_->FindUsedFlexFraction( |
| flexible_sized_tracks_index_, direction_, free_space); |
| |
| LayoutUnit total_growth; |
| Vector<LayoutUnit> increments; |
| increments.Grow(flexible_sized_tracks_index_.size()); |
| ComputeFlexSizedTracksGrowth(flex_fraction, increments, total_growth); |
| |
| if (strategy_->RecomputeUsedFlexFractionIfNeeded(flexible_sized_tracks_index_, |
| flex_fraction, increments, |
| total_growth)) { |
| total_growth = LayoutUnit(0); |
| ComputeFlexSizedTracksGrowth(flex_fraction, increments, total_growth); |
| } |
| |
| size_t i = 0; |
| Vector<GridTrack>& all_tracks = Tracks(direction_); |
| for (auto track_index : flexible_sized_tracks_index_) { |
| auto& track = all_tracks[track_index]; |
| if (LayoutUnit increment = increments[i++]) |
| track.SetBaseSize(track.BaseSize() + increment); |
| } |
| if (this->FreeSpace(direction_)) { |
| SetFreeSpace(direction_, |
| this->FreeSpace(direction_).value() - total_growth); |
| } |
| max_content_size_ += total_growth; |
| } |
| |
| void GridTrackSizingAlgorithm::StretchAutoTracks() { |
| LayoutUnit free_space = strategy_->FreeSpaceForStretchAutoTracksStep(); |
| if (auto_sized_tracks_for_stretch_index_.IsEmpty() || (free_space <= 0) || |
| (layout_grid_->ContentAlignment(direction_).Distribution() != |
| kContentDistributionStretch)) |
| return; |
| |
| unsigned number_of_auto_sized_tracks = |
| auto_sized_tracks_for_stretch_index_.size(); |
| LayoutUnit size_to_increase = free_space / number_of_auto_sized_tracks; |
| Vector<GridTrack>& all_tracks = Tracks(direction_); |
| for (const auto& track_index : auto_sized_tracks_for_stretch_index_) { |
| auto& track = all_tracks[track_index]; |
| LayoutUnit base_size = track.BaseSize() + size_to_increase; |
| track.SetBaseSize(base_size); |
| } |
| SetFreeSpace(direction_, LayoutUnit()); |
| } |
| |
| void GridTrackSizingAlgorithm::AdvanceNextState() { |
| switch (sizing_state_) { |
| case kColumnSizingFirstIteration: |
| sizing_state_ = kRowSizingFirstIteration; |
| return; |
| case kRowSizingFirstIteration: |
| sizing_state_ = kColumnSizingSecondIteration; |
| return; |
| case kColumnSizingSecondIteration: |
| sizing_state_ = kRowSizingSecondIteration; |
| return; |
| case kRowSizingSecondIteration: |
| sizing_state_ = kColumnSizingFirstIteration; |
| return; |
| } |
| NOTREACHED(); |
| sizing_state_ = kColumnSizingFirstIteration; |
| } |
| |
| bool GridTrackSizingAlgorithm::IsValidTransition() const { |
| switch (sizing_state_) { |
| case kColumnSizingFirstIteration: |
| case kColumnSizingSecondIteration: |
| return direction_ == kForColumns; |
| case kRowSizingFirstIteration: |
| case kRowSizingSecondIteration: |
| return direction_ == kForRows; |
| } |
| NOTREACHED(); |
| return false; |
| } |
| |
| void GridTrackSizingAlgorithm::Setup(GridTrackSizingDirection direction, |
| size_t num_tracks, |
| Optional<LayoutUnit> available_space) { |
| DCHECK(needs_setup_); |
| direction_ = direction; |
| SetAvailableSpace( |
| direction, available_space ? available_space.value().ClampNegativeToZero() |
| : available_space); |
| |
| if (available_space) { |
| strategy_ = WTF::MakeUnique<DefiniteSizeStrategy>(*this); |
| } else { |
| strategy_ = WTF::MakeUnique<IndefiniteSizeStrategy>(*this); |
| is_in_perform_layout_ = |
| layout_grid_->GetDocument().View()->IsInPerformLayout(); |
| } |
| |
| content_sized_tracks_index_.Shrink(0); |
| flexible_sized_tracks_index_.Shrink(0); |
| auto_sized_tracks_for_stretch_index_.Shrink(0); |
| |
| if (available_space) { |
| LayoutUnit gutters_size = layout_grid_->GuttersSize( |
| grid_, direction, 0, grid_.NumTracks(direction), available_space); |
| SetFreeSpace(direction, available_space.value() - gutters_size); |
| } else { |
| SetFreeSpace(direction, WTF::nullopt); |
| } |
| Tracks(direction).resize(num_tracks); |
| |
| needs_setup_ = false; |
| } |
| |
| // Described in https://drafts.csswg.org/css-grid/#algo-track-sizing |
| void GridTrackSizingAlgorithm::Run() { |
| StateMachine state_machine(*this); |
| |
| // Step 1. |
| Optional<LayoutUnit> initial_free_space = FreeSpace(direction_); |
| InitializeTrackSizes(); |
| |
| // Step 2. |
| if (!content_sized_tracks_index_.IsEmpty()) |
| ResolveIntrinsicTrackSizes(); |
| |
| // This is not exactly a step of the track sizing algorithm, but we use the |
| // track sizes computed |
| // up to this moment (before maximization) to calculate the grid container |
| // intrinsic sizes. |
| ComputeGridContainerIntrinsicSizes(); |
| |
| if (FreeSpace(direction_)) { |
| LayoutUnit updated_free_space = |
| FreeSpace(direction_).value() - min_content_size_; |
| SetFreeSpace(direction_, updated_free_space); |
| if (updated_free_space <= 0) |
| return; |
| } |
| |
| // Step 3. |
| strategy_->MaximizeTracks(Tracks(direction_), direction_ == kForColumns |
| ? free_space_columns_ |
| : free_space_rows_); |
| |
| // Step 4. |
| StretchFlexibleTracks(initial_free_space); |
| |
| // Step 5. |
| StretchAutoTracks(); |
| } |
| |
| void GridTrackSizingAlgorithm::Reset() { |
| sizing_state_ = kColumnSizingFirstIteration; |
| columns_.Shrink(0); |
| rows_.Shrink(0); |
| content_sized_tracks_index_.Shrink(0); |
| flexible_sized_tracks_index_.Shrink(0); |
| auto_sized_tracks_for_stretch_index_.Shrink(0); |
| SetAvailableSpace(kForRows, WTF::nullopt); |
| SetAvailableSpace(kForColumns, WTF::nullopt); |
| } |
| |
| #if DCHECK_IS_ON() |
| bool GridTrackSizingAlgorithm::TracksAreWiderThanMinTrackBreadth() const { |
| const Vector<GridTrack>& all_tracks = Tracks(direction_); |
| for (size_t i = 0; i < all_tracks.size(); ++i) { |
| GridTrackSize track_size = GetGridTrackSize(direction_, i); |
| if (InitialBaseSize(track_size) > all_tracks[i].BaseSize()) |
| return false; |
| } |
| return true; |
| } |
| #endif |
| |
| GridTrackSizingAlgorithm::StateMachine::StateMachine( |
| GridTrackSizingAlgorithm& algorithm) |
| : algorithm_(algorithm) { |
| DCHECK(algorithm_.IsValidTransition()); |
| DCHECK(!algorithm_.needs_setup_); |
| } |
| |
| GridTrackSizingAlgorithm::StateMachine::~StateMachine() { |
| algorithm_.AdvanceNextState(); |
| algorithm_.needs_setup_ = true; |
| } |
| |
| } // namespace blink |