blob: bbfb39a99cba8feb6108c74c63a58080a34da94f [file] [log] [blame]
// 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/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,
GridTrackSizingDirection) 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;
};
class DefiniteSizeStrategy final : public GridTrackSizingAlgorithmStrategy {
public:
DefiniteSizeStrategy(GridTrackSizingAlgorithm& algorithm)
: GridTrackSizingAlgorithmStrategy(algorithm) {}
private:
LayoutUnit MinLogicalWidthForChild(LayoutBox&,
Length child_min_size,
GridTrackSizingDirection) 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;
}
};
// TODO(svillar): Repeated in LayoutGrid.
LayoutUnit GridTrackSizingAlgorithmStrategy::ComputeMarginLogicalSizeForChild(
MarginDirection for_direction,
const LayoutGrid* grid,
const LayoutBox& child) {
if (!child.StyleRef().HasMargin())
return LayoutUnit();
bool is_row_axis = for_direction == kInlineDirection;
LayoutUnit margin_start;
LayoutUnit margin_end;
LayoutUnit logical_size =
is_row_axis ? child.LogicalWidth() : child.LogicalHeight();
Length margin_start_length = is_row_axis ? child.StyleRef().MarginStart()
: child.StyleRef().MarginBefore();
Length margin_end_length = is_row_axis ? child.StyleRef().MarginEnd()
: child.StyleRef().MarginAfter();
child.ComputeMarginsForDirection(
for_direction, grid, child.ContainingBlockLogicalWidthForContent(),
logical_size, margin_start, margin_end, margin_start_length,
margin_end_length);
return margin_start + margin_end;
}
bool GridTrackSizingAlgorithmStrategy::
HasOverrideContainingBlockContentSizeForChild(
const LayoutBox& child,
GridTrackSizingDirection direction) {
return direction == kForColumns
? child.HasOverrideContainingBlockLogicalWidth()
: child.HasOverrideContainingBlockLogicalHeight();
}
LayoutUnit
GridTrackSizingAlgorithmStrategy::OverrideContainingBlockContentSizeForChild(
const LayoutBox& child,
GridTrackSizingDirection direction) {
return direction == kForColumns
? child.OverrideContainingBlockContentLogicalWidth()
: child.OverrideContainingBlockContentLogicalHeight();
}
bool GridTrackSizingAlgorithmStrategy::
ShouldClearOverrideContainingBlockContentSizeForChild(
const LayoutBox& child,
GridTrackSizingDirection direction) {
if (direction == kForColumns) {
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);
}
GridTrackSizingDirection
GridTrackSizingAlgorithmStrategy::FlowAwareDirectionForChild(
const LayoutGrid* layout_grid,
const LayoutBox& child,
GridTrackSizingDirection direction) {
return child.IsHorizontalWritingMode() ==
layout_grid->IsHorizontalWritingMode()
? direction
: (direction == kForColumns ? kForRows : kForColumns);
}
LayoutUnit GridTrackSizingAlgorithm::AssumedRowsSizeForOrthogonalChild(
const LayoutBox& child) const {
DCHECK(layout_grid_->IsOrthogonalChild(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(), sizing_operation_);
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(), sizing_operation_);
return grid_area_breadth;
}
bool GridTrackSizingAlgorithmStrategy::
UpdateOverrideContainingBlockContentSizeForChild(
LayoutBox& child,
GridTrackSizingDirection direction) const {
LayoutUnit override_size =
algorithm_.GridAreaBreadthForChild(child, direction);
if (HasOverrideContainingBlockContentSizeForChild(child, direction) &&
OverrideContainingBlockContentSizeForChild(child, direction) ==
override_size)
return false;
SetOverrideContainingBlockContentSizeForChild(child, direction,
override_size);
return true;
}
LayoutUnit GridTrackSizingAlgorithmStrategy::LogicalHeightForChild(
LayoutBox& child) const {
GridTrackSizingDirection child_block_direction =
FlowAwareDirectionForChild(GetLayoutGrid(), child, kForRows);
// If |child| has a relative logical height, we shouldn't let it override its
// intrinsic height, which is what we are interested in here. Thus we need to
// set the block-axis override size to -1 (no possible resolution).
if (ShouldClearOverrideContainingBlockContentSizeForChild(child, kForRows)) {
SetOverrideContainingBlockContentSizeForChild(child, child_block_direction,
LayoutUnit(-1));
child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis);
}
// We need to clear the stretched height to properly compute logical height
// during layout.
if (child.NeedsLayout())
child.ClearOverrideLogicalContentHeight();
child.LayoutIfNeeded();
GridAxis baseline_axis = GetLayoutGrid()->IsOrthogonalChild(child)
? kGridRowAxis
: kGridColumnAxis;
if (GetLayoutGrid()->IsBaselineAlignmentForChild(child, baseline_axis) &&
GetLayoutGrid()->IsBaselineContextComputed(baseline_axis)) {
auto& group =
GetLayoutGrid()->GetBaselineGroupForChild(child, baseline_axis);
return group.MaxAscent() + group.MaxDescent();
}
return child.LogicalHeight() + child.MarginLogicalHeight();
}
DISABLE_CFI_PERF
LayoutUnit GridTrackSizingAlgorithmStrategy::MinContentForChild(
LayoutBox& child) const {
GridTrackSizingDirection child_inline_direction =
FlowAwareDirectionForChild(GetLayoutGrid(), child, kForColumns);
if (Direction() == child_inline_direction) {
// If |child| has a relative logical width, we shouldn't let it override its
// intrinsic width, which is what we are interested in here. Thus we need to
// set the inline-axis override size to -1 (no possible resolution).
if (ShouldClearOverrideContainingBlockContentSizeForChild(child,
kForColumns)) {
SetOverrideContainingBlockContentSizeForChild(
child, child_inline_direction, LayoutUnit(-1));
}
// 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
LayoutUnit margin_logical_width =
child.NeedsLayout() ? ComputeMarginLogicalSizeForChild(
kInlineDirection, GetLayoutGrid(), child)
: child.MarginLogicalWidth();
return child.MinPreferredLogicalWidth() + margin_logical_width;
}
if (Direction() == kForColumns &&
algorithm_.sizing_operation_ == kIntrinsicSizeComputation) {
DCHECK(GetLayoutGrid()->IsOrthogonalChild(child));
if (GetLayoutGrid()->IsBaselineAlignmentForChild(child, kGridRowAxis) &&
GetLayoutGrid()->IsBaselineContextComputed(kGridRowAxis)) {
auto& group =
GetLayoutGrid()->GetBaselineGroupForChild(child, kGridRowAxis);
return group.MaxAscent() + group.MaxDescent();
}
}
if (UpdateOverrideContainingBlockContentSizeForChild(child,
child_inline_direction))
child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis);
return LogicalHeightForChild(child);
}
DISABLE_CFI_PERF
LayoutUnit GridTrackSizingAlgorithmStrategy::MaxContentForChild(
LayoutBox& child) const {
GridTrackSizingDirection child_inline_direction =
FlowAwareDirectionForChild(GetLayoutGrid(), child, kForColumns);
if (Direction() == child_inline_direction) {
// If |child| has a relative logical width, we shouldn't let it override its
// intrinsic width, which is what we are interested in here. Thus we need to
// set the inline-axis override size to -1 (no possible resolution).
if (ShouldClearOverrideContainingBlockContentSizeForChild(child,
kForColumns)) {
SetOverrideContainingBlockContentSizeForChild(
child, child_inline_direction, LayoutUnit(-1));
}
// 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
LayoutUnit margin_logical_width =
child.NeedsLayout() ? ComputeMarginLogicalSizeForChild(
kInlineDirection, GetLayoutGrid(), child)
: child.MarginLogicalWidth();
return child.MaxPreferredLogicalWidth() + margin_logical_width;
}
if (UpdateOverrideContainingBlockContentSizeForChild(child,
child_inline_direction))
child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis);
return LogicalHeightForChild(child);
}
LayoutUnit GridTrackSizingAlgorithmStrategy::MinSizeForChild(
LayoutBox& child) const {
GridTrackSizingDirection child_inline_direction =
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)) {
if (child.IsLayoutReplaced() && child_size.IsAuto()) {
// If the box has an aspect ratio and no specified size, its automatic
// minimum size is the smaller of its content size and its transferred
// size.
return is_row_axis ? std::min(child.IntrinsicLogicalWidth(),
MinContentForChild(child))
: std::min(child.IntrinsicLogicalHeight(),
MinContentForChild(child));
}
return MinContentForChild(child);
}
bool override_size_has_changed =
UpdateOverrideContainingBlockContentSizeForChild(child,
child_inline_direction);
if (is_row_axis)
return MinLogicalWidthForChild(child, child_min_size,
child_inline_direction);
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);
}
LayoutUnit DefiniteSizeStrategy::MinLogicalWidthForChild(
LayoutBox& child,
Length child_min_size,
GridTrackSizingDirection child_inline_direction) const {
LayoutUnit margin_logical_width = ComputeMarginLogicalSizeForChild(
kInlineDirection, GetLayoutGrid(), child);
return child.ComputeLogicalWidthUsing(
kMinSize, child_min_size,
OverrideContainingBlockContentSizeForChild(child,
child_inline_direction),
GetLayoutGrid()) +
margin_logical_width;
}
void DefiniteSizeStrategy::LayoutGridItemForMinSizeComputation(
LayoutBox& child,
bool override_size_has_changed) const {
if (override_size_has_changed)
child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis);
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 IndefiniteSizeStrategy::MinLogicalWidthForChild(
LayoutBox& child,
Length child_min_size,
GridTrackSizingDirection child_inline_direction) 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,
OverrideContainingBlockContentSizeForChild(child,
child_inline_direction),
GetLayoutGrid()) +
margin_logical_width;
}
void IndefiniteSizeStrategy::LayoutGridItemForMinSizeComputation(
LayoutBox& child,
bool override_size_has_changed) const {
if (override_size_has_changed && Direction() != kForColumns)
child.SetNeedsLayout(LayoutInvalidationReason::kGridChanged, kMarkOnlyThis);
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, kTrackSizing)
.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),
kIntrinsicSizeComputation);
size_t number_of_tracks = algorithm_.Tracks(Direction()).size();
flex_fraction = FindFrUnitSize(
GridSpan::TranslatedDefiniteGridSpan(0, number_of_tracks), free_space);
return true;
}
Optional<LayoutUnit> GridTrackSizingAlgorithm::FreeSpace(
GridTrackSizingDirection direction) {
return direction == kForRows ? free_space_rows_ : free_space_columns_;
}
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 {
return GetGridTrackSize(direction, translated_index, sizing_operation_);
}
GridTrackSize GridTrackSizingAlgorithm::GetGridTrackSize(
GridTrackSizingDirection direction,
size_t translated_index,
SizingOperation sizing_operation) 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()) {
// For the inline axis this only happens when we're computing the intrinsic
// sizes.
if ((sizing_operation == kIntrinsicSizeComputation) ||
(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,
available_space_.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,
available_space_.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());
Vector<GridTrack>& track_list = Tracks(direction_);
bool has_definite_free_space = !!available_space_;
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(), available_space_.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);
}
}
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(),
available_space_.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(), sizing_operation_);
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::kNullopt);
}
}
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(),
sizing_operation_);
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) {
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::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,
SizingOperation sizing_operation,
Optional<LayoutUnit> available_space,
Optional<LayoutUnit> free_space) {
DCHECK(needs_setup_);
DCHECK_EQ(!!available_space, !!free_space);
direction_ = direction;
available_space_ = available_space
? available_space.value().ClampNegativeToZero()
: available_space;
sizing_operation_ = sizing_operation;
if (available_space)
strategy_ = WTF::MakeUnique<DefiniteSizeStrategy>(*this);
else
strategy_ = WTF::MakeUnique<IndefiniteSizeStrategy>(*this);
content_sized_tracks_index_.Shrink(0);
flexible_sized_tracks_index_.Shrink(0);
SetFreeSpace(direction, free_space);
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_);
if (flexible_sized_tracks_index_.IsEmpty())
return;
// Step 4.
StretchFlexibleTracks(initial_free_space);
}
void GridTrackSizingAlgorithm::Reset() {
sizing_state_ = kColumnSizingFirstIteration;
columns_.Shrink(0);
rows_.Shrink(0);
content_sized_tracks_index_.Shrink(0);
flexible_sized_tracks_index_.Shrink(0);
}
#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