Skip to content

Commit ca91505

Browse files
committed
layout: Implement the fit-content() sizing function
Spec: https://drafts.csswg.org/css-sizing-3/#funcdef-width-fit-content It's similar to the `fit-content` keyword but, instead of clamping the stretch size between `min-content` and `max-content`, it clamps the provided argument. So now that we support `fit-content`, it's quite straightforward to add. It's just not completely clear what should happen when the argument has a cyclic percentage, so this may need some further adjustments depending on the outcome of w3c/csswg-drafts#11805 Signed-off-by: Oriol Brufau <[email protected]>
1 parent 86341da commit ca91505

34 files changed

+86
-194
lines changed

Cargo.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,27 +117,27 @@ rustls-pemfile = "2.0"
117117
rustls-pki-types = "1.11"
118118
script_layout_interface = { path = "components/shared/script_layout" }
119119
script_traits = { path = "components/shared/script" }
120-
selectors = { git = "https://github.com/servo/stylo", branch = "2025-03-15" }
120+
selectors = { git = "https://github.com/servo/stylo", rev = "refs/pull/153/head" }
121121
serde = "1.0.219"
122122
serde_bytes = "0.11"
123123
serde_json = "1.0"
124124
servo-media = { git = "https://github.com/servo/media" }
125125
servo-media-dummy = { git = "https://github.com/servo/media" }
126126
servo-media-gstreamer = { git = "https://github.com/servo/media" }
127-
servo_arc = { git = "https://github.com/servo/stylo", branch = "2025-03-15" }
128-
stylo_atoms = { git = "https://github.com/servo/stylo", branch = "2025-03-15" }
127+
servo_arc = { git = "https://github.com/servo/stylo", rev = "refs/pull/153/head" }
128+
stylo_atoms = { git = "https://github.com/servo/stylo", rev = "refs/pull/153/head" }
129129
smallbitvec = "2.6.0"
130130
smallvec = "1.14"
131131
static_assertions = "1.1"
132132
string_cache = "0.8"
133133
string_cache_codegen = "0.5"
134134
strum = "0.26"
135135
strum_macros = "0.26"
136-
stylo = { git = "https://github.com/servo/stylo", branch = "2025-03-15" }
137-
stylo_config = { git = "https://github.com/servo/stylo", branch = "2025-03-15" }
138-
stylo_dom = { git = "https://github.com/servo/stylo", branch = "2025-03-15" }
139-
stylo_malloc_size_of = { git = "https://github.com/servo/stylo", branch = "2025-03-15" }
140-
stylo_traits = { git = "https://github.com/servo/stylo", branch = "2025-03-15" }
136+
stylo = { git = "https://github.com/servo/stylo", rev = "refs/pull/153/head" }
137+
stylo_config = { git = "https://github.com/servo/stylo", rev = "refs/pull/153/head" }
138+
stylo_dom = { git = "https://github.com/servo/stylo", rev = "refs/pull/153/head" }
139+
stylo_malloc_size_of = { git = "https://github.com/servo/stylo", rev = "refs/pull/153/head" }
140+
stylo_traits = { git = "https://github.com/servo/stylo", rev = "refs/pull/153/head" }
141141
surfman = { git = "https://github.com/servo/surfman", rev = "f7688b4585f9e0b5d4bf8ee8e4a91e82349610b1", features = ["chains"] }
142142
syn = { version = "2", default-features = false, features = ["clone-impls", "derive", "parsing"] }
143143
synstructure = "0.13"

components/layout_2020/flexbox/layout.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2599,11 +2599,10 @@ impl FlexItemBox {
25992599
BoxSizing::BorderBox => length - main_padding_border_sum,
26002600
}
26012601
};
2602-
size.maybe_map(|v| {
2602+
size.maybe_map_for_preferred(|v| {
26032603
v.maybe_to_used_value(container_definite_main_size)
26042604
.map(apply_box_sizing)
26052605
})
2606-
.unwrap_or_default()
26072606
},
26082607
},
26092608
}

components/layout_2020/flow/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2144,9 +2144,11 @@ impl<'container> PlacementState<'container> {
21442144

21452145
fn block_size_is_zero_or_intrinsic(size: &StyleSize, containing_block: &ContainingBlock) -> bool {
21462146
match size {
2147-
StyleSize::Auto | StyleSize::MinContent | StyleSize::MaxContent | StyleSize::FitContent => {
2148-
true
2149-
},
2147+
StyleSize::Auto |
2148+
StyleSize::MinContent |
2149+
StyleSize::MaxContent |
2150+
StyleSize::FitContent |
2151+
StyleSize::FitContentFunction(_) => true,
21502152
StyleSize::Stretch => {
21512153
// TODO: Should this return true when the containing block has a definite size of 0px?
21522154
!containing_block.size.block.is_definite()

components/layout_2020/geom.rs

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,17 @@ impl<T: Clone> LogicalVec2<T> {
133133
block: f(&self.block),
134134
}
135135
}
136+
137+
pub(crate) fn map_with<U, V>(
138+
&self,
139+
other: &LogicalVec2<U>,
140+
f: impl Fn(&T, &U) -> V,
141+
) -> LogicalVec2<V> {
142+
LogicalVec2 {
143+
inline: f(&self.inline, &other.inline),
144+
block: f(&self.block, &other.block),
145+
}
146+
}
136147
}
137148

138149
impl<T: Add<Output = T> + Copy> Add<LogicalVec2<T>> for LogicalVec2<T> {
@@ -697,6 +708,8 @@ pub(crate) enum Size<T> {
697708
MaxContent,
698709
/// <https://drafts.csswg.org/css-sizing-4/#valdef-width-fit-content>
699710
FitContent,
711+
/// <https://drafts.csswg.org/css-sizing-3/#funcdef-width-fit-content>
712+
FitContentFunction(T),
700713
/// <https://drafts.csswg.org/css-sizing-4/#valdef-width-stretch>
701714
Stretch,
702715
/// Represents a numeric `<length-percentage>`, but resolved as a `T`.
@@ -730,34 +743,50 @@ impl<T: Clone> Size<T> {
730743
}
731744

732745
#[inline]
733-
pub fn map<U>(&self, f: impl FnOnce(T) -> U) -> Size<U> {
746+
pub(crate) fn map<U>(&self, f: impl FnOnce(T) -> U) -> Size<U> {
734747
match self {
735748
Size::Initial => Size::Initial,
736749
Size::MinContent => Size::MinContent,
737750
Size::MaxContent => Size::MaxContent,
738751
Size::FitContent => Size::FitContent,
752+
Size::FitContentFunction(size) => Size::FitContentFunction(f(size.clone())),
739753
Size::Stretch => Size::Stretch,
740754
Size::Numeric(numeric) => Size::Numeric(f(numeric.clone())),
741755
}
742756
}
743757

744758
#[inline]
745-
pub fn maybe_map<U>(&self, f: impl FnOnce(T) -> Option<U>) -> Option<Size<U>> {
746-
Some(match self {
747-
Size::Numeric(numeric) => Size::Numeric(f(numeric.clone())?),
759+
pub(crate) fn maybe_map_for_preferred<U>(&self, f: impl FnOnce(T) -> Option<U>) -> Size<U> {
760+
match self {
761+
Size::Numeric(numeric) => f(numeric.clone()).map_or(Size::Initial, Size::Numeric),
762+
Size::FitContentFunction(numeric) => {
763+
f(numeric.clone()).map_or(Size::FitContent, Size::FitContentFunction)
764+
},
748765
_ => self.map(|_| unreachable!("This shouldn't be called for keywords")),
749-
})
766+
}
767+
}
768+
769+
#[inline]
770+
pub(crate) fn maybe_map_for_max<U>(&self, f: impl FnOnce(T) -> Option<U>) -> Size<U> {
771+
match self {
772+
Size::Numeric(numeric) => f(numeric.clone()).map_or(Size::Initial, Size::Numeric),
773+
Size::FitContentFunction(numeric) => {
774+
f(numeric.clone()).map_or(Size::MaxContent, Size::FitContentFunction)
775+
},
776+
_ => self.map(|_| unreachable!("This shouldn't be called for keywords")),
777+
}
750778
}
751779
}
752780

753781
impl From<StyleSize> for Size<LengthPercentage> {
754782
fn from(size: StyleSize) -> Self {
755783
match size {
756-
StyleSize::LengthPercentage(length) => Size::Numeric(length.0),
784+
StyleSize::LengthPercentage(lp) => Size::Numeric(lp.0),
757785
StyleSize::Auto => Size::Initial,
758786
StyleSize::MinContent => Size::MinContent,
759787
StyleSize::MaxContent => Size::MaxContent,
760788
StyleSize::FitContent => Size::FitContent,
789+
StyleSize::FitContentFunction(lp) => Size::FitContentFunction(lp.0),
761790
StyleSize::Stretch => Size::Stretch,
762791
StyleSize::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"),
763792
}
@@ -767,11 +796,12 @@ impl From<StyleSize> for Size<LengthPercentage> {
767796
impl From<StyleMaxSize> for Size<LengthPercentage> {
768797
fn from(max_size: StyleMaxSize) -> Self {
769798
match max_size {
770-
StyleMaxSize::LengthPercentage(length) => Size::Numeric(length.0),
799+
StyleMaxSize::LengthPercentage(lp) => Size::Numeric(lp.0),
771800
StyleMaxSize::None => Size::Initial,
772801
StyleMaxSize::MinContent => Size::MinContent,
773802
StyleMaxSize::MaxContent => Size::MaxContent,
774803
StyleMaxSize::FitContent => Size::FitContent,
804+
StyleMaxSize::FitContentFunction(lp) => Size::FitContentFunction(lp.0),
775805
StyleMaxSize::Stretch => Size::Stretch,
776806
StyleMaxSize::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"),
777807
}
@@ -787,22 +817,6 @@ impl Size<LengthPercentage> {
787817
}
788818

789819
impl LogicalVec2<Size<LengthPercentage>> {
790-
pub(crate) fn maybe_percentages_relative_to_basis(
791-
&self,
792-
basis: &LogicalVec2<Option<Au>>,
793-
) -> LogicalVec2<Size<Au>> {
794-
LogicalVec2 {
795-
inline: self
796-
.inline
797-
.maybe_map(|v| v.maybe_to_used_value(basis.inline))
798-
.unwrap_or_default(),
799-
block: self
800-
.block
801-
.maybe_map(|v| v.maybe_to_used_value(basis.block))
802-
.unwrap_or_default(),
803-
}
804-
}
805-
806820
pub(crate) fn percentages_relative_to_basis(
807821
&self,
808822
basis: &LogicalVec2<Au>,
@@ -831,6 +845,7 @@ impl Size<Au> {
831845
},
832846
Self::MinContent => content_size.min_content,
833847
Self::MaxContent => content_size.max_content,
848+
Self::FitContentFunction(size) => content_size.shrink_to_fit(*size),
834849
Self::FitContent => {
835850
content_size.shrink_to_fit(stretch_size.unwrap_or_else(|| content_size.max_content))
836851
},
@@ -852,6 +867,7 @@ impl Size<Au> {
852867
Self::Initial => get_automatic_minimum_size(),
853868
Self::MinContent => content_size.min_content,
854869
Self::MaxContent => content_size.max_content,
870+
Self::FitContentFunction(size) => content_size.shrink_to_fit(*size),
855871
Self::FitContent => content_size.shrink_to_fit(stretch_size.unwrap_or_default()),
856872
Self::Stretch => stretch_size.unwrap_or_default(),
857873
Self::Numeric(numeric) => *numeric,
@@ -870,6 +886,7 @@ impl Size<Au> {
870886
Self::Initial => return None,
871887
Self::MinContent => content_size.min_content,
872888
Self::MaxContent => content_size.max_content,
889+
Self::FitContentFunction(size) => content_size.shrink_to_fit(*size),
873890
Self::FitContent => content_size.shrink_to_fit(stretch_size.unwrap_or(MAX_AU)),
874891
Self::Stretch => return stretch_size,
875892
Self::Numeric(numeric) => *numeric,
@@ -888,7 +905,11 @@ impl Size<Au> {
888905
#[inline]
889906
pub(crate) fn maybe_resolve_extrinsic(&self, stretch_size: Option<Au>) -> Option<Au> {
890907
match self {
891-
Self::Initial | Self::MinContent | Self::MaxContent | Self::FitContent => None,
908+
Self::Initial |
909+
Self::MinContent |
910+
Self::MaxContent |
911+
Self::FitContent |
912+
Self::FitContentFunction(_) => None,
892913
Self::Stretch => stretch_size,
893914
Self::Numeric(numeric) => Some(*numeric),
894915
}

components/layout_2020/sizing.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ pub(crate) fn outer_inline(
191191
content_size.sizes.max_content,
192192
content_size.depends_on_block_constraints,
193193
),
194+
Size::FitContentFunction(size) => {
195+
let size = content_size.sizes.shrink_to_fit(size);
196+
(size, size, content_size.depends_on_block_constraints)
197+
},
194198
Size::Stretch => return stretch_values,
195199
})
196200
};

components/layout_2020/style_ext.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -992,18 +992,16 @@ impl LayoutStyle<'_> {
992992
depends_on_block_constraints(&max_size.block) ||
993993
style.depends_on_block_constraints_due_to_relative_positioning(writing_mode);
994994

995-
let box_size = box_size.maybe_percentages_relative_to_basis(&containing_block.size);
996-
let content_box_size = style
997-
.content_box_size_for_box_size(box_size, &pbm)
998-
.map(|v| v.map(Au::from));
995+
let box_size = box_size.map_with(&containing_block.size, |size, basis| {
996+
size.maybe_map_for_preferred(|v| v.maybe_to_used_value(*basis))
997+
});
998+
let content_box_size = style.content_box_size_for_box_size(box_size, &pbm);
999999
let min_size = min_size.percentages_relative_to_basis(&containing_block_size_or_zero);
1000-
let content_min_box_size = style
1001-
.content_min_box_size_for_min_size(min_size, &pbm)
1002-
.map(|v| v.map(Au::from));
1003-
let max_size = max_size.maybe_percentages_relative_to_basis(&containing_block.size);
1004-
let content_max_box_size = style
1005-
.content_max_box_size_for_max_size(max_size, &pbm)
1006-
.map(|v| v.map(Au::from));
1000+
let content_min_box_size = style.content_min_box_size_for_min_size(min_size, &pbm);
1001+
let max_size = max_size.map_with(&containing_block.size, |size, basis| {
1002+
size.maybe_map_for_max(|v| v.maybe_to_used_value(*basis))
1003+
});
1004+
let content_max_box_size = style.content_max_box_size_for_max_size(max_size, &pbm);
10071005
ContentBoxSizesAndPBM {
10081006
content_box_sizes: LogicalVec2 {
10091007
block: Sizes::new(

components/layout_2020/taffy/stylo_taffy/convert.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ pub fn dimension(val: &stylo::Size) -> taffy::Dimension {
5454
stylo::Size::MaxContent => taffy::Dimension::Auto,
5555
stylo::Size::MinContent => taffy::Dimension::Auto,
5656
stylo::Size::FitContent => taffy::Dimension::Auto,
57+
stylo::Size::FitContentFunction(_) => taffy::Dimension::Auto,
5758
stylo::Size::Stretch => taffy::Dimension::Auto,
5859

5960
// Anchor positioning will be flagged off for time being
@@ -71,6 +72,7 @@ pub fn max_size_dimension(val: &stylo::MaxSize) -> taffy::Dimension {
7172
stylo::MaxSize::MaxContent => taffy::Dimension::Auto,
7273
stylo::MaxSize::MinContent => taffy::Dimension::Auto,
7374
stylo::MaxSize::FitContent => taffy::Dimension::Auto,
75+
stylo::MaxSize::FitContentFunction(_) => taffy::Dimension::Auto,
7476
stylo::MaxSize::Stretch => taffy::Dimension::Auto,
7577

7678
// Anchor positioning will be flagged off for time being

tests/wpt/meta/css/css-sizing/fit-content-length-percentage-001.html.ini

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/wpt/meta/css/css-sizing/fit-content-length-percentage-002.html.ini

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/wpt/meta/css/css-sizing/fit-content-length-percentage-003.html.ini

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/wpt/meta/css/css-sizing/fit-content-length-percentage-004.html.ini

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/wpt/meta/css/css-sizing/fit-content-length-percentage-005.html.ini

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/wpt/meta/css/css-sizing/fit-content-length-percentage-006.html.ini

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/wpt/meta/css/css-sizing/fit-content-length-percentage-007.html.ini

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)