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
...@@ -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