Commit 8d3d941a authored by Oriol Brufau's avatar Oriol Brufau Committed by Commit Bot

[css-grid] Accommodate spanning items crossing flexible tracks

The specification added a step to increase sizes to accommodate spanning
items crossing flexible tracks instead of ignoring their contents
completely. This is done after handling all grid items that don't cross
such tracks, and this time items are considered together, not grouped by
their span size.

Spec: https://drafts.csswg.org/css-grid/#algo-spanning-flex-items

BUG=935102

TEST=third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/grid-flex-track-intrinsic-sizes-001.html
TEST=third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/grid-flex-track-intrinsic-sizes-002.html
TEST=third_party/blink/web_tests/fast/css-grid-layout/flex-and-content-sized-resolution-columns.html
TEST=third_party/blink/web_tests/fast/css-grid-layout/grid-gutters-and-flex-content.html

Change-Id: Ic91a2eb471fae23cde1570c461f491cd5335d771
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1492337Reviewed-by: default avatarJavier Fernandez <jfernandez@igalia.com>
Reviewed-by: default avatarSergio Villar <svillar@igalia.com>
Commit-Queue: Oriol Brufau <obrufau@igalia.com>
Cr-Commit-Position: refs/heads/master@{#705228}
parent 7fc25637
...@@ -69,6 +69,11 @@ void GridTrack::SetGrowthLimitCap(base::Optional<LayoutUnit> growth_limit_cap) { ...@@ -69,6 +69,11 @@ void GridTrack::SetGrowthLimitCap(base::Optional<LayoutUnit> growth_limit_cap) {
growth_limit_cap_ = growth_limit_cap; growth_limit_cap_ = growth_limit_cap;
} }
void GridTrack::SetSizeDistributionWeight(double size_distribution_weight) {
DCHECK_GE(size_distribution_weight, 0);
size_distribution_weight_ = size_distribution_weight;
}
bool GridTrack::IsGrowthLimitBiggerThanBaseSize() const { bool GridTrack::IsGrowthLimitBiggerThanBaseSize() const {
return GrowthLimitIsInfinite() || growth_limit_ >= base_size_; return GrowthLimitIsInfinite() || growth_limit_ >= base_size_;
} }
...@@ -521,7 +526,8 @@ double GridTrackSizingAlgorithmStrategy::FindFrUnitSize( ...@@ -521,7 +526,8 @@ double GridTrackSizingAlgorithmStrategy::FindFrUnitSize(
void GridTrackSizingAlgorithmStrategy::DistributeSpaceToTracks( void GridTrackSizingAlgorithmStrategy::DistributeSpaceToTracks(
Vector<GridTrack*>& tracks, Vector<GridTrack*>& tracks,
LayoutUnit& available_logical_space) const { LayoutUnit& available_logical_space) const {
algorithm_.DistributeSpaceToTracks<kMaximizeTracks>(tracks, nullptr, algorithm_.DistributeSpaceToTracks<kNotCrossingIntrinsicFlexibleTracks,
kMaximizeTracks>(tracks, nullptr,
available_logical_space); available_logical_space);
} }
...@@ -962,8 +968,10 @@ LayoutUnit GridTrackSizingAlgorithm::InitialGrowthLimit( ...@@ -962,8 +968,10 @@ LayoutUnit GridTrackSizingAlgorithm::InitialGrowthLimit(
const GridTrackSize& track_size, const GridTrackSize& track_size,
LayoutUnit base_size) const { LayoutUnit base_size) const {
const GridLength& grid_length = track_size.MaxTrackBreadth(); const GridLength& grid_length = track_size.MaxTrackBreadth();
if (grid_length.IsFlex()) if (grid_length.IsFlex()) {
return base_size; return track_size.MinTrackBreadth().IsContentSized() ? LayoutUnit(kInfinity)
: base_size;
}
const Length& track_length = grid_length.length(); const Length& track_length = grid_length.length();
if (track_length.IsSpecified()) { if (track_length.IsSpecified()) {
...@@ -1000,8 +1008,11 @@ void GridTrackSizingAlgorithm::InitializeTrackSizes() { ...@@ -1000,8 +1008,11 @@ void GridTrackSizingAlgorithm::InitializeTrackSizes() {
if (track_size.IsContentSized()) if (track_size.IsContentSized())
content_sized_tracks_index_.push_back(i); content_sized_tracks_index_.push_back(i);
if (track_size.MaxTrackBreadth().IsFlex()) if (track_size.MaxTrackBreadth().IsFlex()) {
flexible_sized_tracks_index_.push_back(i); flexible_sized_tracks_index_.push_back(i);
if (track_size.MinTrackBreadth().IsContentSized())
track.SetSizeDistributionWeight(track_size.MaxTrackBreadth().Flex());
}
if (track_size.HasAutoMaxTrackBreadth() && !track_size.IsFitContent()) if (track_size.HasAutoMaxTrackBreadth() && !track_size.IsFitContent())
auto_sized_tracks_for_stretch_index_.push_back(i); auto_sized_tracks_for_stretch_index_.push_back(i);
...@@ -1047,12 +1058,12 @@ void GridTrackSizingAlgorithm::SizeTrackToFitNonSpanningItem( ...@@ -1047,12 +1058,12 @@ void GridTrackSizingAlgorithm::SizeTrackToFitNonSpanningItem(
} }
} }
bool GridTrackSizingAlgorithm::SpanningItemCrossesFlexibleSizedTracks( bool GridTrackSizingAlgorithm::SpanningItemCrossesIntrinsicFlexibleSizedTracks(
const GridSpan& span) const { const GridSpan& span) const {
for (const auto& track_position : span) { for (const auto& track_position : span) {
const GridTrackSize& track_size = const GridTrackSize& track_size =
GetGridTrackSize(direction_, track_position); GetGridTrackSize(direction_, track_position);
if (track_size.MinTrackBreadth().IsFlex() || if (track_size.HasIntrinsicMinTrackBreadth() &&
track_size.MaxTrackBreadth().IsFlex()) track_size.MaxTrackBreadth().IsFlex())
return true; return true;
} }
...@@ -1114,9 +1125,13 @@ static LayoutUnit TrackSizeForTrackSizeComputationPhase( ...@@ -1114,9 +1125,13 @@ static LayoutUnit TrackSizeForTrackSizeComputationPhase(
return track.BaseSize(); return track.BaseSize();
} }
static bool ShouldProcessTrackForTrackSizeComputationPhase( static bool ShouldProcessTrackForTrackSizeComputationVariantAndPhase(
TrackSizeComputationVariant variant,
TrackSizeComputationPhase phase, TrackSizeComputationPhase phase,
const GridTrackSize& track_size) { const GridTrackSize& track_size) {
if (variant == kCrossingIntrinsicFlexibleTracks &&
!track_size.MaxTrackBreadth().IsFlex())
return false;
switch (phase) { switch (phase) {
case kResolveIntrinsicMinimums: case kResolveIntrinsicMinimums:
return track_size.HasIntrinsicMinTrackBreadth(); return track_size.HasIntrinsicMinTrackBreadth();
...@@ -1245,6 +1260,12 @@ static bool SortByGridTrackGrowthPotential(const GridTrack* track1, ...@@ -1245,6 +1260,12 @@ static bool SortByGridTrackGrowthPotential(const GridTrack* track1,
track2_has_infinite_growth_potential_without_cap) track2_has_infinite_growth_potential_without_cap)
return track2_has_infinite_growth_potential_without_cap; return track2_has_infinite_growth_potential_without_cap;
// We don't have to take weights into account when comparing growth potentials
// because they must be 0 at this point. Flexible tracks with a greater
// weight have already been handled due to their infinite growth limit.
DCHECK_EQ(track1->SizeDistributionWeight(), 0);
DCHECK_EQ(track2->SizeDistributionWeight(), 0);
LayoutUnit track1_limit = LayoutUnit track1_limit =
track1->GrowthLimitCap().value_or(track1->GrowthLimit()); track1->GrowthLimitCap().value_or(track1->GrowthLimit());
LayoutUnit track2_limit = LayoutUnit track2_limit =
...@@ -1267,7 +1288,21 @@ static void ClampGrowthShareIfNeeded(TrackSizeComputationPhase phase, ...@@ -1267,7 +1288,21 @@ static void ClampGrowthShareIfNeeded(TrackSizeComputationPhase phase,
growth_share = std::min(growth_share, distance_to_cap); growth_share = std::min(growth_share, distance_to_cap);
} }
template <TrackSizeComputationPhase phase> static Vector<double> FractionsOfRemainingSpace(
const Vector<GridTrack*>& tracks) {
size_t tracks_size = tracks.size();
Vector<double> fractions_of_remaining_space(tracks_size);
double weight_sum = 0;
for (size_t i = tracks_size; i-- > 0;) {
double weight = tracks[i]->SizeDistributionWeight();
weight_sum += weight;
fractions_of_remaining_space[i] =
weight_sum > 0 ? weight / weight_sum : 1.0 / (tracks_size - i);
}
return fractions_of_remaining_space;
}
template <TrackSizeComputationVariant variant, TrackSizeComputationPhase phase>
void GridTrackSizingAlgorithm::DistributeSpaceToTracks( void GridTrackSizingAlgorithm::DistributeSpaceToTracks(
Vector<GridTrack*>& tracks, Vector<GridTrack*>& tracks,
Vector<GridTrack*>* grow_beyond_growth_limits_tracks, Vector<GridTrack*>* grow_beyond_growth_limits_tracks,
...@@ -1280,13 +1315,23 @@ void GridTrackSizingAlgorithm::DistributeSpaceToTracks( ...@@ -1280,13 +1315,23 @@ void GridTrackSizingAlgorithm::DistributeSpaceToTracks(
} }
if (available_logical_space > 0) { if (available_logical_space > 0) {
std::sort(tracks.begin(), tracks.end(), SortByGridTrackGrowthPotential); // No need to sort when distributing intrinsic contributions among flexible
// tracks, because all of them have an infinite growth potential.
if (variant == kNotCrossingIntrinsicFlexibleTracks)
std::sort(tracks.begin(), tracks.end(), SortByGridTrackGrowthPotential);
auto fractions_of_remaining_space = FractionsOfRemainingSpace(tracks);
size_t tracks_size = tracks.size(); size_t tracks_size = tracks.size();
for (size_t i = 0; i < tracks_size; ++i) { for (size_t i = 0; i < tracks_size; ++i) {
GridTrack& track = *tracks[i]; GridTrack& track = *tracks[i];
LayoutUnit available_logical_space_share = #if DCHECK_IS_ON()
available_logical_space / (tracks_size - i); if (variant == kCrossingIntrinsicFlexibleTracks)
DCHECK(track.GrowthLimitIsInfinite());
else
DCHECK_EQ(track.SizeDistributionWeight(), 0);
#endif
LayoutUnit available_logical_space_share(available_logical_space *
fractions_of_remaining_space[i]);
const LayoutUnit& track_breadth = const LayoutUnit& track_breadth =
TrackSizeForTrackSizeComputationPhase(phase, track, kForbidInfinity); TrackSizeForTrackSizeComputationPhase(phase, track, kForbidInfinity);
LayoutUnit growth_share = LayoutUnit growth_share =
...@@ -1304,6 +1349,9 @@ void GridTrackSizingAlgorithm::DistributeSpaceToTracks( ...@@ -1304,6 +1349,9 @@ void GridTrackSizingAlgorithm::DistributeSpaceToTracks(
} }
if (available_logical_space > 0 && grow_beyond_growth_limits_tracks) { if (available_logical_space > 0 && grow_beyond_growth_limits_tracks) {
// We never grow flex tracks beyond growth limits, since they are infinite.
DCHECK_NE(variant, kCrossingIntrinsicFlexibleTracks);
// We need to sort them because there might be tracks with growth limit caps // 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 // (like the ones with fit-content()) which cannot indefinitely grow over
// the limits. // the limits.
...@@ -1313,12 +1361,15 @@ void GridTrackSizingAlgorithm::DistributeSpaceToTracks( ...@@ -1313,12 +1361,15 @@ void GridTrackSizingAlgorithm::DistributeSpaceToTracks(
SortByGridTrackGrowthPotential); SortByGridTrackGrowthPotential);
} }
auto fractions_of_remaining_space =
FractionsOfRemainingSpace(*grow_beyond_growth_limits_tracks);
size_t tracks_growing_above_max_breadth_size = size_t tracks_growing_above_max_breadth_size =
grow_beyond_growth_limits_tracks->size(); grow_beyond_growth_limits_tracks->size();
for (size_t i = 0; i < tracks_growing_above_max_breadth_size; ++i) { for (size_t i = 0; i < tracks_growing_above_max_breadth_size; ++i) {
GridTrack* track = grow_beyond_growth_limits_tracks->at(i); GridTrack* track = grow_beyond_growth_limits_tracks->at(i);
LayoutUnit growth_share = LayoutUnit growth_share(available_logical_space *
available_logical_space / (tracks_growing_above_max_breadth_size - i); fractions_of_remaining_space[i]);
ClampGrowthShareIfNeeded(phase, *track, growth_share); ClampGrowthShareIfNeeded(phase, *track, growth_share);
DCHECK_GE(growth_share, 0) << "We must never shrink any grid track or " DCHECK_GE(growth_share, 0) << "We must never shrink any grid track or "
"else we can't guarantee we abide by our " "else we can't guarantee we abide by our "
...@@ -1336,7 +1387,7 @@ void GridTrackSizingAlgorithm::DistributeSpaceToTracks( ...@@ -1336,7 +1387,7 @@ void GridTrackSizingAlgorithm::DistributeSpaceToTracks(
} }
} }
template <TrackSizeComputationPhase phase> template <TrackSizeComputationVariant variant, TrackSizeComputationPhase phase>
void GridTrackSizingAlgorithm::IncreaseSizesToAccommodateSpanningItems( void GridTrackSizingAlgorithm::IncreaseSizesToAccommodateSpanningItems(
const GridItemsSpanGroupRange& grid_items_with_span) { const GridItemsSpanGroupRange& grid_items_with_span) {
Vector<GridTrack>& all_tracks = Tracks(direction_); Vector<GridTrack>& all_tracks = Tracks(direction_);
...@@ -1351,8 +1402,9 @@ void GridTrackSizingAlgorithm::IncreaseSizesToAccommodateSpanningItems( ...@@ -1351,8 +1402,9 @@ void GridTrackSizingAlgorithm::IncreaseSizesToAccommodateSpanningItems(
for (auto* it = grid_items_with_span.range_start; for (auto* it = grid_items_with_span.range_start;
it != grid_items_with_span.range_end; ++it) { it != grid_items_with_span.range_end; ++it) {
GridItemWithSpan& grid_item_with_span = *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(); const GridSpan& item_span = grid_item_with_span.GetGridSpan();
DCHECK(variant == kCrossingIntrinsicFlexibleTracks ||
item_span.IntegerSpan() > 1u);
grow_beyond_growth_limits_tracks.Shrink(0); grow_beyond_growth_limits_tracks.Shrink(0);
filtered_tracks.Shrink(0); filtered_tracks.Shrink(0);
...@@ -1362,7 +1414,8 @@ void GridTrackSizingAlgorithm::IncreaseSizesToAccommodateSpanningItems( ...@@ -1362,7 +1414,8 @@ void GridTrackSizingAlgorithm::IncreaseSizesToAccommodateSpanningItems(
GridTrack& track = Tracks(direction_)[track_position]; GridTrack& track = Tracks(direction_)[track_position];
spanning_tracks_size += spanning_tracks_size +=
TrackSizeForTrackSizeComputationPhase(phase, track, kForbidInfinity); TrackSizeForTrackSizeComputationPhase(phase, track, kForbidInfinity);
if (!ShouldProcessTrackForTrackSizeComputationPhase(phase, track_size)) if (!ShouldProcessTrackForTrackSizeComputationVariantAndPhase(
variant, phase, track_size))
continue; continue;
filtered_tracks.push_back(&track); filtered_tracks.push_back(&track);
...@@ -1387,7 +1440,7 @@ void GridTrackSizingAlgorithm::IncreaseSizesToAccommodateSpanningItems( ...@@ -1387,7 +1440,7 @@ void GridTrackSizingAlgorithm::IncreaseSizesToAccommodateSpanningItems(
grow_beyond_growth_limits_tracks.IsEmpty() grow_beyond_growth_limits_tracks.IsEmpty()
? filtered_tracks ? filtered_tracks
: grow_beyond_growth_limits_tracks; : grow_beyond_growth_limits_tracks;
DistributeSpaceToTracks<phase>( DistributeSpaceToTracks<variant, phase>(
filtered_tracks, &tracks_to_grow_beyond_growth_limits, extra_space); filtered_tracks, &tracks_to_grow_beyond_growth_limits, extra_space);
} }
...@@ -1398,8 +1451,25 @@ void GridTrackSizingAlgorithm::IncreaseSizesToAccommodateSpanningItems( ...@@ -1398,8 +1451,25 @@ void GridTrackSizingAlgorithm::IncreaseSizesToAccommodateSpanningItems(
} }
} }
template <TrackSizeComputationVariant variant>
void GridTrackSizingAlgorithm::IncreaseSizesToAccommodateSpanningItems(
const GridItemsSpanGroupRange& grid_items_with_span) {
IncreaseSizesToAccommodateSpanningItems<variant, kResolveIntrinsicMinimums>(
grid_items_with_span);
IncreaseSizesToAccommodateSpanningItems<variant,
kResolveContentBasedMinimums>(
grid_items_with_span);
IncreaseSizesToAccommodateSpanningItems<variant, kResolveMaxContentMinimums>(
grid_items_with_span);
IncreaseSizesToAccommodateSpanningItems<variant, kResolveIntrinsicMaximums>(
grid_items_with_span);
IncreaseSizesToAccommodateSpanningItems<variant, kResolveMaxContentMaximums>(
grid_items_with_span);
}
void GridTrackSizingAlgorithm::ResolveIntrinsicTrackSizes() { void GridTrackSizingAlgorithm::ResolveIntrinsicTrackSizes() {
Vector<GridItemWithSpan> items_sorted_by_increasing_span; Vector<GridItemWithSpan> items_sorted_by_increasing_span;
Vector<GridItemWithSpan> items_crossing_flexible_tracks;
if (grid_.HasGridItems()) { if (grid_.HasGridItems()) {
HashSet<LayoutBox*> items_set; HashSet<LayoutBox*> items_set;
for (const auto& track_index : content_sized_tracks_index_) { for (const auto& track_index : content_sized_tracks_index_) {
...@@ -1408,9 +1478,12 @@ void GridTrackSizingAlgorithm::ResolveIntrinsicTrackSizes() { ...@@ -1408,9 +1478,12 @@ void GridTrackSizingAlgorithm::ResolveIntrinsicTrackSizes() {
while (auto* grid_item = iterator->NextGridItem()) { while (auto* grid_item = iterator->NextGridItem()) {
if (items_set.insert(grid_item).is_new_entry) { if (items_set.insert(grid_item).is_new_entry) {
const GridSpan& span = grid_.GridItemSpan(*grid_item, direction_); const GridSpan& span = grid_.GridItemSpan(*grid_item, direction_);
if (span.IntegerSpan() == 1) { if (SpanningItemCrossesIntrinsicFlexibleSizedTracks(span)) {
items_crossing_flexible_tracks.push_back(
GridItemWithSpan(*grid_item, span));
} else if (span.IntegerSpan() == 1) {
SizeTrackToFitNonSpanningItem(span, *grid_item, track); SizeTrackToFitNonSpanningItem(span, *grid_item, track);
} else if (!SpanningItemCrossesFlexibleSizedTracks(span)) { } else {
items_sorted_by_increasing_span.push_back( items_sorted_by_increasing_span.push_back(
GridItemWithSpan(*grid_item, span)); GridItemWithSpan(*grid_item, span));
} }
...@@ -1426,24 +1499,23 @@ void GridTrackSizingAlgorithm::ResolveIntrinsicTrackSizes() { ...@@ -1426,24 +1499,23 @@ void GridTrackSizingAlgorithm::ResolveIntrinsicTrackSizes() {
while (it != end) { while (it != end) {
GridItemsSpanGroupRange span_group_range = {it, GridItemsSpanGroupRange span_group_range = {it,
std::upper_bound(it, end, *it)}; std::upper_bound(it, end, *it)};
IncreaseSizesToAccommodateSpanningItems<kResolveIntrinsicMinimums>( IncreaseSizesToAccommodateSpanningItems<
span_group_range); kNotCrossingIntrinsicFlexibleTracks>(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; it = span_group_range.range_end;
} }
IncreaseSizesToAccommodateSpanningItems<kCrossingIntrinsicFlexibleTracks>(
{items_crossing_flexible_tracks.begin(),
items_crossing_flexible_tracks.end()});
Vector<GridTrack>& track_list = Tracks(direction_);
for (const auto& track_index : content_sized_tracks_index_) { for (const auto& track_index : content_sized_tracks_index_) {
GridTrack& track = Tracks(direction_)[track_index]; GridTrack& track = track_list[track_index];
if (track.GrowthLimit() == kInfinity) if (track.GrowthLimit() == kInfinity)
track.SetGrowthLimit(track.BaseSize()); track.SetGrowthLimit(track.BaseSize());
} }
for (const auto& track_index : flexible_sized_tracks_index_)
track_list[track_index].SetSizeDistributionWeight(0);
} }
void GridTrackSizingAlgorithm::ComputeGridContainerIntrinsicSizes() { void GridTrackSizingAlgorithm::ComputeGridContainerIntrinsicSizes() {
...@@ -1451,8 +1523,12 @@ void GridTrackSizingAlgorithm::ComputeGridContainerIntrinsicSizes() { ...@@ -1451,8 +1523,12 @@ void GridTrackSizingAlgorithm::ComputeGridContainerIntrinsicSizes() {
Vector<GridTrack>& all_tracks = Tracks(direction_); Vector<GridTrack>& all_tracks = Tracks(direction_);
for (auto& track : all_tracks) { for (auto& track : all_tracks) {
DCHECK(strategy_->IsComputingSizeContainment() || #if DCHECK_IS_ON()
!track.InfiniteGrowthPotential()); if (!strategy_->IsComputingSizeContainment()) {
DCHECK(!track.InfiniteGrowthPotential());
DCHECK_EQ(track.SizeDistributionWeight(), 0);
}
#endif
min_content_size_ += track.BaseSize(); min_content_size_ += track.BaseSize();
max_content_size_ += max_content_size_ +=
track.GrowthLimitIsInfinite() ? track.BaseSize() : track.GrowthLimit(); track.GrowthLimitIsInfinite() ? track.BaseSize() : track.GrowthLimit();
......
...@@ -24,6 +24,11 @@ class Grid; ...@@ -24,6 +24,11 @@ class Grid;
class GridTrackSizingAlgorithmStrategy; class GridTrackSizingAlgorithmStrategy;
class LayoutGrid; class LayoutGrid;
enum TrackSizeComputationVariant {
kNotCrossingIntrinsicFlexibleTracks,
kCrossingIntrinsicFlexibleTracks,
};
enum TrackSizeComputationPhase { enum TrackSizeComputationPhase {
kResolveIntrinsicMinimums, kResolveIntrinsicMinimums,
kResolveContentBasedMinimums, kResolveContentBasedMinimums,
...@@ -66,6 +71,14 @@ class GridTrack { ...@@ -66,6 +71,14 @@ class GridTrack {
} }
void SetGrowthLimitCap(base::Optional<LayoutUnit>); void SetGrowthLimitCap(base::Optional<LayoutUnit>);
// For flexible tracks, intrinsic contributions are distributed according to
// the ratios of the flex fractions. At that point we will only have some
// GridTracks, but we won't know their index, so we won't be able to call
// GetGridTrackSize in order to obtain their flex fraction. Therefore we cache
// them instead of computing on demand.
double SizeDistributionWeight() const { return size_distribution_weight_; }
void SetSizeDistributionWeight(double);
private: private:
bool IsGrowthLimitBiggerThanBaseSize() const; bool IsGrowthLimitBiggerThanBaseSize() const;
void EnsureGrowthLimitIsBiggerThanBaseSize(); void EnsureGrowthLimitIsBiggerThanBaseSize();
...@@ -76,6 +89,7 @@ class GridTrack { ...@@ -76,6 +89,7 @@ class GridTrack {
LayoutUnit size_during_distribution_; LayoutUnit size_during_distribution_;
base::Optional<LayoutUnit> growth_limit_cap_; base::Optional<LayoutUnit> growth_limit_cap_;
bool infinitely_growable_; bool infinitely_growable_;
double size_distribution_weight_{0};
}; };
class GridTrackSizingAlgorithm final { class GridTrackSizingAlgorithm final {
...@@ -150,14 +164,19 @@ class GridTrackSizingAlgorithm final { ...@@ -150,14 +164,19 @@ class GridTrackSizingAlgorithm final {
void SizeTrackToFitNonSpanningItem(const GridSpan&, void SizeTrackToFitNonSpanningItem(const GridSpan&,
LayoutBox& grid_item, LayoutBox& grid_item,
GridTrack&); GridTrack&);
bool SpanningItemCrossesFlexibleSizedTracks(const GridSpan&) const; bool SpanningItemCrossesIntrinsicFlexibleSizedTracks(const GridSpan&) const;
typedef struct GridItemsSpanGroupRange GridItemsSpanGroupRange; typedef struct GridItemsSpanGroupRange GridItemsSpanGroupRange;
template <TrackSizeComputationPhase phase> template <TrackSizeComputationVariant variant,
TrackSizeComputationPhase phase>
void IncreaseSizesToAccommodateSpanningItems(
const GridItemsSpanGroupRange& grid_items_with_span);
template <TrackSizeComputationVariant variant>
void IncreaseSizesToAccommodateSpanningItems( void IncreaseSizesToAccommodateSpanningItems(
const GridItemsSpanGroupRange& grid_items_with_span); const GridItemsSpanGroupRange& grid_items_with_span);
LayoutUnit ItemSizeForTrackSizeComputationPhase(TrackSizeComputationPhase, LayoutUnit ItemSizeForTrackSizeComputationPhase(TrackSizeComputationPhase,
LayoutBox&) const; LayoutBox&) const;
template <TrackSizeComputationPhase phase> template <TrackSizeComputationVariant variant,
TrackSizeComputationPhase phase>
void DistributeSpaceToTracks( void DistributeSpaceToTracks(
Vector<GridTrack*>& tracks, Vector<GridTrack*>& tracks,
Vector<GridTrack*>* grow_beyond_growth_limits_tracks, Vector<GridTrack*>* grow_beyond_growth_limits_tracks,
......
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Grid Layout Test: Intrinsic contribution of an item with flex tracks</title>
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-grid/#algo-spanning-items" title="11.5.3 Increase sizes to accommodate spanning items crossing content-sized tracks">
<link rel="help" href="https://drafts.csswg.org/css-grid/#algo-spanning-flex-items" title="11.5.4 Increase sizes to accommodate spanning items crossing flexible tracks">
<meta name="assert" content="This test checks that the intrinsic contribution of a single grid item is distributed correctly among the tracks it spans when flexible tracks are involved.">
<style>
#grid {
display: grid;
width: 50px;
height: 50px;
border: solid;
}
#item {
width: 100px;
height: 100px;
background: blue;
}
</style>
<div id="log"></div>
<div id="grid">
<div id="item"></div>
</div>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../grid-definition/support/testing-utils.js"></script>
<script>
const item = document.getElementById("item");
function checkTrackSizes(span, trackList, expected) {
item.style.gridColumn = item.style.gridRow = `span ${span}`;
TestingUtils.testGridTemplateColumnsRows("grid", trackList, trackList, expected, expected);
}
// Item spanning an intrinsic flexible track
checkTrackSizes(1, "0fr", "100px");
checkTrackSizes(1, "1fr", "100px");
checkTrackSizes(1, "2fr", "100px");
// Item spanning a fixed flexible track
checkTrackSizes(1, "minmax(0, 0fr)", "0px");
checkTrackSizes(1, "minmax(0, .5fr)", "25px");
checkTrackSizes(1, "minmax(0, 1fr)", "50px");
checkTrackSizes(1, "minmax(0, 2fr)", "50px");
checkTrackSizes(1, "minmax(75px, 1fr)", "75px");
// Item spanning 2 intrinsic flexible tracks
checkTrackSizes(2, "0fr 0fr", "50px 50px");
checkTrackSizes(2, "0fr 1fr", "0px 100px");
checkTrackSizes(2, "1fr 0fr", "100px 0px");
checkTrackSizes(2, "1fr 1fr", "50px 50px");
checkTrackSizes(2, "1fr 3fr", "25px 75px");
checkTrackSizes(2, "0fr 0fr 1fr", "50px 50px 0px");
// Item spanning 2 fixed flexible tracks
checkTrackSizes(2, "minmax(0, 0fr) minmax(0, 0fr)", "0px 0px");
checkTrackSizes(2, "minmax(0, 0fr) minmax(0, 1fr)", "0px 50px");
checkTrackSizes(2, "minmax(15px, 0fr) minmax(0, 1fr)", "15px 35px");
checkTrackSizes(2, "minmax(20px, 1fr) minmax(0, 1fr)", "25px 25px");
checkTrackSizes(2, "minmax(30px, 1fr) minmax(0, 1fr)", "30px 20px");
// Item spanning an intrinsic flexible track and a fixed flexible track
checkTrackSizes(2, "0fr minmax(0, 0fr)", "100px 0px");
checkTrackSizes(2, "0fr minmax(0, 1fr)", "100px 0px");
checkTrackSizes(2, "1fr minmax(0, 1fr)", "100px 0px");
checkTrackSizes(2, "1fr minmax(25px, 1fr)", "75px 25px");
// Item spanning an intrinsic flexible track and an intrinsic non-flexible track
checkTrackSizes(2, "0fr auto", "100px 0px");
checkTrackSizes(2, "1fr auto", "100px 0px");
checkTrackSizes(2, "1fr max-content", "100px 0px");
// Item spanning a fixed flexible track and an intrinsic non-flexible track
checkTrackSizes(2, "minmax(0, 0fr) auto", "0px 100px");
checkTrackSizes(2, "minmax(0, 1fr) auto", "0px 100px");
checkTrackSizes(2, "minmax(25px, 0fr) auto", "25px 75px");
checkTrackSizes(2, "minmax(25px, 1fr) auto", "25px 75px");
</script>
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Grid Layout Test: Intrinsic contributions of 2 items with flex tracks</title>
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-grid/#algo-spanning-items" title="11.5.3 Increase sizes to accommodate spanning items crossing content-sized tracks">
<link rel="help" href="https://drafts.csswg.org/css-grid/#algo-spanning-flex-items" title="11.5.4 Increase sizes to accommodate spanning items crossing flexible tracks">
<meta name="assert" content="This test checks that the intrinsic contributions of 2 items are distributed in the right order when flexible tracks are involved.">
<style>
#grid {
display: grid;
grid-template-areas: ". . . ."
". a . ."
". . . ."
". . . b";
width: 50px;
height: 50px;
border: solid;
}
#item1 {
grid-column: 1 / a;
grid-row: 1 / a;
width: 60px;
height: 60px;
background: blue;
}
#item2 {
grid-column: a / b;
grid-row: a / b;
width: 150px;
height: 150px;
background: yellow;
}
</style>
<div id="log"></div>
<div id="grid">
<div id="item1"></div>
<div id="item2"></div>
</div>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../grid-definition/support/testing-utils.js"></script>
<script>
function checkTrackSizes(trackList, expected) {
TestingUtils.testGridTemplateColumnsRows("grid", trackList, trackList, expected, expected);
}
// We have a symmetric grid with 2 items and 4 tracks, as follows:
// ╔═╤═╗─┬─┐
// ╟─╔═╬═╪═╗
// ╚═╬═╝─┼─╢
// ├─╫─┼─┼─╢
// └─╚═╧═╧═╝
// The 1st item has spans less tracks (2) than the 2nd item (3),
// therefore its contribution (60px) is distributed first.
// All the 60px go to the 2nd track, since the 1st track is not intrinsic.
// Then the 2nd item only needs to distribute 150px-60px=90px
// among the 3rd and 4th tracks.
checkTrackSizes("minmax(0, 1fr) auto auto auto", "0px 60px 45px 45px");
// The 1st item now spans a flexible track with an intrinsic minimum,
// therefore its contribution (60px) is distributed last.
// The 2nd item distributes its contribution (150px) among the 2nd, 3rd and 4th tracks.
// Then the 1st item only needs to distribute 60px-50px=10px to the 1st track.
checkTrackSizes("1fr auto auto auto", "10px 50px 50px 50px");
// Now both items span a flexible track with an intrinsic minimum,
// so their contributions are handled simultaneously,
// even if the 1st item still spans less tracks than the 2nd one.
// Therefore the distribution is as follows:
// - 1st track: 60px/2 = 30px
// - 2nd track: max(60px/2, 150px/3) = 50px
// - 3rd track: 150px/3 = 50px
// - 4th track: 150px/3 = 50px
checkTrackSizes("1fr 1fr 1fr 1fr", "30px 50px 50px 50px");
// Like the previous case, but with different flex ratios:
// - 1st track: 60px/2 = 30px
// - 2nd track: max(60px/2, 150px/6) = 30px
// - 3rd track: 150px/6 = 25px
// - 4th track: 150px*4/6 = 100px
checkTrackSizes("1fr 1fr 1fr 4fr", "30px 30px 25px 100px");
// Change the grid as follows:
// ╔═╦═╤═╗
// ╠═╝─┼─╢
// ╟─┼─┼─╢
// ╚═╧═╧═╝
document.getElementById("grid").style.gridTemplateAreas = `
"a . ."
". . ."
". . b"`;
// Now the 1st item has a span of 1, so usually we would handle its contribution
// at the very beginning, before items that span multiple tracks.
// But not if its track is flexible, then it's still handled at the end,
// simultaneously with other items that span some intrinsic flexible track.
// - 1nd track: max(60px, 150px/3) = 60px
// - 2nd track: 150px/3 = 50px
// - 3rd track: 150px/3 = 50px
checkTrackSizes("1fr 1fr 1fr", "60px 50px 50px");
</script>
PASS window.getComputedStyle(gridFixedAndMinContentAndFlex, '').getPropertyValue('grid-template-columns') is "20px 30px 50px" PASS window.getComputedStyle(gridFixedAndMinContentAndFlex, '').getPropertyValue('grid-template-columns') is "20px 30px 50px"
PASS window.getComputedStyle(gridFixedAndMinContentAndFlexMultipleOverlap, '').getPropertyValue('grid-template-columns') is "20px 10px 70px" PASS window.getComputedStyle(gridFixedAndMinContentAndFlexMultipleOverlap, '').getPropertyValue('grid-template-columns') is "20px 10px 70px"
PASS window.getComputedStyle(gridMinMaxFixedFlexAndMaxContentAndAuto, '').getPropertyValue('grid-template-columns') is "60px 20px 20px" PASS window.getComputedStyle(gridMinMaxFixedFlexAndMaxContentAndAuto, '').getPropertyValue('grid-template-columns') is "30px 50px 20px"
PASS window.getComputedStyle(gridMinMaxFixedFlexAndMaxContentAndAutoNoFlexSpanningItems, '').getPropertyValue('grid-template-columns') is "100px 0px 0px" PASS window.getComputedStyle(gridMinMaxFixedFlexAndMaxContentAndAutoNoFlexSpanningItems, '').getPropertyValue('grid-template-columns') is "30px 70px 0px"
PASS window.getComputedStyle(gridMinMaxAutoFixedAndMinContentAndFixed, '').getPropertyValue('grid-template-columns') is "35px 20px 25px" PASS window.getComputedStyle(gridMinMaxAutoFixedAndMinContentAndFixed, '').getPropertyValue('grid-template-columns') is "35px 20px 25px"
PASS window.getComputedStyle(gridMinContentAndMinMaxFixedMinContentAndFlex, '').getPropertyValue('grid-template-columns') is "20px 20px 60px" PASS window.getComputedStyle(gridMinContentAndMinMaxFixedMinContentAndFlex, '').getPropertyValue('grid-template-columns') is "20px 20px 60px"
PASS window.getComputedStyle(gridMaxContentAndMinMaxFixedMaxContentAndFlex, '').getPropertyValue('grid-template-columns') is "70px 20px 10px" PASS window.getComputedStyle(gridMaxContentAndMinMaxFixedMaxContentAndFlex, '').getPropertyValue('grid-template-columns') is "70px 20px 30px"
PASS successfullyParsed is true PASS successfullyParsed is true
TEST COMPLETE TEST COMPLETE
......
...@@ -98,11 +98,11 @@ function checkColumns(gridId, columnValue) ...@@ -98,11 +98,11 @@ function checkColumns(gridId, columnValue)
checkColumns("gridFixedAndMinContentAndFlex", "20px 30px 50px"); checkColumns("gridFixedAndMinContentAndFlex", "20px 30px 50px");
checkColumns("gridFixedAndMinContentAndFlexMultipleOverlap", "20px 10px 70px"); checkColumns("gridFixedAndMinContentAndFlexMultipleOverlap", "20px 10px 70px");
checkColumns("gridMinMaxFixedFlexAndMaxContentAndAuto", "60px 20px 20px"); checkColumns("gridMinMaxFixedFlexAndMaxContentAndAuto", "30px 50px 20px");
checkColumns("gridMinMaxFixedFlexAndMaxContentAndAutoNoFlexSpanningItems", "100px 0px 0px"); checkColumns("gridMinMaxFixedFlexAndMaxContentAndAutoNoFlexSpanningItems", "30px 70px 0px");
checkColumns("gridMinMaxAutoFixedAndMinContentAndFixed", "35px 20px 25px"); checkColumns("gridMinMaxAutoFixedAndMinContentAndFixed", "35px 20px 25px");
checkColumns("gridMinContentAndMinMaxFixedMinContentAndFlex", "20px 20px 60px"); checkColumns("gridMinContentAndMinMaxFixedMinContentAndFlex", "20px 20px 60px");
checkColumns("gridMaxContentAndMinMaxFixedMaxContentAndFlex", "70px 20px 10px"); checkColumns("gridMaxContentAndMinMaxFixedMaxContentAndFlex", "70px 20px 30px");
</script> </script>
</html> </html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment