From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- servo/components/style/values/generics/grid.rs | 867 +++++++++++++++++++++++++ 1 file changed, 867 insertions(+) create mode 100644 servo/components/style/values/generics/grid.rs (limited to 'servo/components/style/values/generics/grid.rs') diff --git a/servo/components/style/values/generics/grid.rs b/servo/components/style/values/generics/grid.rs new file mode 100644 index 0000000000..22fe249c83 --- /dev/null +++ b/servo/components/style/values/generics/grid.rs @@ -0,0 +1,867 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! Generic types for the handling of +//! [grids](https://drafts.csswg.org/css-grid/). + +use crate::parser::{Parse, ParserContext}; +use crate::values::specified; +use crate::values::{CSSFloat, CustomIdent}; +use crate::{One, Zero}; +use cssparser::Parser; +use std::fmt::{self, Write}; +use std::{cmp, usize}; +use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; + +/// These are the limits that we choose to clamp grid line numbers to. +/// http://drafts.csswg.org/css-grid/#overlarge-grids +/// line_num is clamped to this range at parse time. +pub const MIN_GRID_LINE: i32 = -10000; +/// See above. +pub const MAX_GRID_LINE: i32 = 10000; + +/// A `` type. +/// +/// +#[derive( + Clone, + Debug, + Default, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +pub struct GenericGridLine { + /// A custom identifier for named lines, or the empty atom otherwise. + /// + /// + pub ident: CustomIdent, + /// Denotes the nth grid line from grid item's placement. + /// + /// This is clamped by MIN_GRID_LINE and MAX_GRID_LINE. + /// + /// NOTE(emilio): If we ever allow animating these we need to either do + /// something more complicated for the clamping, or do this clamping at + /// used-value time. + pub line_num: Integer, + /// Flag to check whether it's a `span` keyword. + pub is_span: bool, +} + +pub use self::GenericGridLine as GridLine; + +impl GridLine +where + Integer: PartialEq + Zero, +{ + /// The `auto` value. + pub fn auto() -> Self { + Self { + is_span: false, + line_num: Zero::zero(), + ident: CustomIdent(atom!("")), + } + } + + /// Check whether this `` represents an `auto` value. + pub fn is_auto(&self) -> bool { + self.ident.0 == atom!("") && self.line_num.is_zero() && !self.is_span + } + + /// Check whether this `` represents a `` value. + pub fn is_ident_only(&self) -> bool { + self.ident.0 != atom!("") && self.line_num.is_zero() && !self.is_span + } + + /// Check if `self` makes `other` omittable according to the rules at: + /// https://drafts.csswg.org/css-grid/#propdef-grid-column + /// https://drafts.csswg.org/css-grid/#propdef-grid-area + pub fn can_omit(&self, other: &Self) -> bool { + if self.is_ident_only() { + self == other + } else { + other.is_auto() + } + } +} + +impl ToCss for GridLine +where + Integer: ToCss + PartialEq + Zero + One, +{ + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + // 1. `auto` + if self.is_auto() { + return dest.write_str("auto"); + } + + // 2. `` + if self.is_ident_only() { + return self.ident.to_css(dest); + } + + // 3. `[ span && [ || ] ]` + let has_ident = self.ident.0 != atom!(""); + if self.is_span { + dest.write_str("span")?; + debug_assert!(!self.line_num.is_zero() || has_ident); + + // We omit `line_num` if + // 1. we don't specify it, or + // 2. it is the default value, i.e. 1.0, and the ident is specified. + // https://drafts.csswg.org/css-grid/#grid-placement-span-int + if !self.line_num.is_zero() && !(self.line_num.is_one() && has_ident) { + dest.write_char(' ')?; + self.line_num.to_css(dest)?; + } + + if has_ident { + dest.write_char(' ')?; + self.ident.to_css(dest)?; + } + return Ok(()); + } + + // 4. `[ | ] && ? ]` + debug_assert!(!self.line_num.is_zero()); + self.line_num.to_css(dest)?; + if has_ident { + dest.write_char(' ')?; + self.ident.to_css(dest)?; + } + Ok(()) + } +} + +impl Parse for GridLine { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let mut grid_line = Self::auto(); + if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() { + return Ok(grid_line); + } + + // | [ && ? ] | [ span && [ || ] ] + // This horror is simply, + // [ span? && [ || ] ] + // And, for some magical reason, "span" should be the first or last value and not in-between. + let mut val_before_span = false; + + for _ in 0..3 { + // Maximum possible entities for + let location = input.current_source_location(); + if input.try_parse(|i| i.expect_ident_matching("span")).is_ok() { + if grid_line.is_span { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + if !grid_line.line_num.is_zero() || grid_line.ident.0 != atom!("") { + val_before_span = true; + } + + grid_line.is_span = true; + } else if let Ok(i) = input.try_parse(|i| specified::Integer::parse(context, i)) { + // FIXME(emilio): Probably shouldn't reject if it's calc()... + let value = i.value(); + if value == 0 || val_before_span || !grid_line.line_num.is_zero() { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + grid_line.line_num = specified::Integer::new(cmp::max( + MIN_GRID_LINE, + cmp::min(value, MAX_GRID_LINE), + )); + } else if let Ok(name) = input.try_parse(|i| CustomIdent::parse(i, &["auto"])) { + if val_before_span || grid_line.ident.0 != atom!("") { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + // NOTE(emilio): `span` is consumed above, so we only need to + // reject `auto`. + grid_line.ident = name; + } else { + break; + } + } + + if grid_line.is_auto() { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + if grid_line.is_span { + if !grid_line.line_num.is_zero() { + if grid_line.line_num.value() <= 0 { + // disallow negative integers for grid spans + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + } else if grid_line.ident.0 == atom!("") { + // integer could be omitted + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + } + + Ok(grid_line) + } +} + +/// A track breadth for explicit grid track sizing. It's generic solely to +/// avoid re-implementing it for the computed type. +/// +/// +#[derive( + Animate, + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C, u8)] +pub enum GenericTrackBreadth { + /// The generic type is almost always a non-negative `` + Breadth(L), + /// A flex fraction specified in `fr` units. + #[css(dimension)] + Fr(CSSFloat), + /// `auto` + Auto, + /// `min-content` + MinContent, + /// `max-content` + MaxContent, +} + +pub use self::GenericTrackBreadth as TrackBreadth; + +impl TrackBreadth { + /// Check whether this is a `` (i.e., it only has ``) + /// + /// + #[inline] + pub fn is_fixed(&self) -> bool { + matches!(*self, TrackBreadth::Breadth(..)) + } +} + +/// A `` type for explicit grid track sizing. Like ``, this is +/// generic only to avoid code bloat. It only takes `` +/// +/// +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[repr(C, u8)] +pub enum GenericTrackSize { + /// A flexible `` + Breadth(GenericTrackBreadth), + /// A `minmax` function for a range over an inflexible `` + /// and a flexible `` + /// + /// + #[css(function)] + Minmax(GenericTrackBreadth, GenericTrackBreadth), + /// A `fit-content` function. + /// + /// This stores a TrackBreadth for convenience, but it can only be a + /// LengthPercentage. + /// + /// + #[css(function)] + FitContent(GenericTrackBreadth), +} + +pub use self::GenericTrackSize as TrackSize; + +impl TrackSize { + /// The initial value. + const INITIAL_VALUE: Self = TrackSize::Breadth(TrackBreadth::Auto); + + /// Returns the initial value. + pub const fn initial_value() -> Self { + Self::INITIAL_VALUE + } + + /// Returns true if `self` is the initial value. + pub fn is_initial(&self) -> bool { + matches!(*self, TrackSize::Breadth(TrackBreadth::Auto)) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585 + } + + /// Check whether this is a `` + /// + /// + pub fn is_fixed(&self) -> bool { + match *self { + TrackSize::Breadth(ref breadth) => breadth.is_fixed(), + // For minmax function, it could be either + // minmax(, ) or minmax(, ), + // and since both variants are a subset of minmax(, ), we only + // need to make sure that they're fixed. So, we don't have to modify the parsing function. + TrackSize::Minmax(ref breadth_1, ref breadth_2) => { + if breadth_1.is_fixed() { + return true; // the second value is always a + } + + match *breadth_1 { + TrackBreadth::Fr(_) => false, // should be at this point + _ => breadth_2.is_fixed(), + } + }, + TrackSize::FitContent(_) => false, + } + } +} + +impl Default for TrackSize { + fn default() -> Self { + Self::initial_value() + } +} + +impl ToCss for TrackSize { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + match *self { + TrackSize::Breadth(ref breadth) => breadth.to_css(dest), + TrackSize::Minmax(ref min, ref max) => { + // According to gecko minmax(auto, ) is equivalent to , + // and both are serialized as . + if let TrackBreadth::Auto = *min { + if let TrackBreadth::Fr(_) = *max { + return max.to_css(dest); + } + } + + dest.write_str("minmax(")?; + min.to_css(dest)?; + dest.write_str(", ")?; + max.to_css(dest)?; + dest.write_char(')') + }, + TrackSize::FitContent(ref lp) => { + dest.write_str("fit-content(")?; + lp.to_css(dest)?; + dest.write_char(')') + }, + } + } +} + +/// A `+`. +/// We use the empty slice as `auto`, and always parse `auto` as an empty slice. +/// This means it's impossible to have a slice containing only one auto item. +#[derive( + Clone, + Debug, + Default, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(transparent)] +pub struct GenericImplicitGridTracks( + #[css(if_empty = "auto", iterable)] pub crate::OwnedSlice, +); + +pub use self::GenericImplicitGridTracks as ImplicitGridTracks; + +impl ImplicitGridTracks { + /// Returns true if current value is same as its initial value (i.e. auto). + pub fn is_initial(&self) -> bool { + debug_assert_ne!( + *self, + ImplicitGridTracks(crate::OwnedSlice::from(vec![Default::default()])) + ); + self.0.is_empty() + } +} + +/// Helper function for serializing identifiers with a prefix and suffix, used +/// for serializing (in grid). +pub fn concat_serialize_idents( + prefix: &str, + suffix: &str, + slice: &[CustomIdent], + sep: &str, + dest: &mut CssWriter, +) -> fmt::Result +where + W: Write, +{ + if let Some((ref first, rest)) = slice.split_first() { + dest.write_str(prefix)?; + first.to_css(dest)?; + for thing in rest { + dest.write_str(sep)?; + thing.to_css(dest)?; + } + + dest.write_str(suffix)?; + } + + Ok(()) +} + +/// The initial argument of the `repeat` function. +/// +/// +#[derive( + Clone, + Copy, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C, u8)] +pub enum RepeatCount { + /// A positive integer. This is allowed only for `` and `` + Number(Integer), + /// An `` keyword allowed only for `` + AutoFill, + /// An `` keyword allowed only for `` + AutoFit, +} + +impl Parse for RepeatCount { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + if let Ok(mut i) = input.try_parse(|i| specified::Integer::parse_positive(context, i)) { + if i.value() > MAX_GRID_LINE { + i = specified::Integer::new(MAX_GRID_LINE); + } + return Ok(RepeatCount::Number(i)); + } + try_match_ident_ignore_ascii_case! { input, + "auto-fill" => Ok(RepeatCount::AutoFill), + "auto-fit" => Ok(RepeatCount::AutoFit), + } + } +} + +/// The structure containing `` and `` values. +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[css(function = "repeat")] +#[repr(C)] +pub struct GenericTrackRepeat { + /// The number of times for the value to be repeated (could also be `auto-fit` or `auto-fill`) + pub count: RepeatCount, + /// `` accompanying `` values. + /// + /// If there's no ``, then it's represented by an empty vector. + /// For N `` values, there will be N+1 ``, and so this vector's + /// length is always one value more than that of the ``. + pub line_names: crate::OwnedSlice>, + /// `` values. + pub track_sizes: crate::OwnedSlice>, +} + +pub use self::GenericTrackRepeat as TrackRepeat; + +impl ToCss for TrackRepeat { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + dest.write_str("repeat(")?; + self.count.to_css(dest)?; + dest.write_str(", ")?; + + let mut line_names_iter = self.line_names.iter(); + for (i, (ref size, ref names)) in self + .track_sizes + .iter() + .zip(&mut line_names_iter) + .enumerate() + { + if i > 0 { + dest.write_char(' ')?; + } + + concat_serialize_idents("[", "] ", names, " ", dest)?; + size.to_css(dest)?; + } + + if let Some(line_names_last) = line_names_iter.next() { + concat_serialize_idents(" [", "]", line_names_last, " ", dest)?; + } + + dest.write_char(')')?; + + Ok(()) + } +} + +/// Track list values. Can be or +#[derive( + Animate, + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C, u8)] +pub enum GenericTrackListValue { + /// A value. + TrackSize(#[animation(field_bound)] GenericTrackSize), + /// A value. + TrackRepeat(#[animation(field_bound)] GenericTrackRepeat), +} + +pub use self::GenericTrackListValue as TrackListValue; + +impl TrackListValue { + // FIXME: can't use TrackSize::initial_value() here b/c rustc error "is not yet stable as a const fn" + const INITIAL_VALUE: Self = TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto)); + + fn is_repeat(&self) -> bool { + matches!(*self, TrackListValue::TrackRepeat(..)) + } + + /// Returns true if `self` is the initial value. + pub fn is_initial(&self) -> bool { + matches!( + *self, + TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto)) + ) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585 + } +} + +impl Default for TrackListValue { + #[inline] + fn default() -> Self { + Self::INITIAL_VALUE + } +} + +/// A grid `` type. +/// +/// +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +pub struct GenericTrackList { + /// The index in `values` where our `` value is, if in bounds. + #[css(skip)] + pub auto_repeat_index: usize, + /// A vector of ` | ` values. + pub values: crate::OwnedSlice>, + /// `` accompanying ` | ` values. + /// + /// If there's no ``, then it's represented by an empty vector. + /// For N values, there will be N+1 ``, and so this vector's + /// length is always one value more than that of the ``. + pub line_names: crate::OwnedSlice>, +} + +pub use self::GenericTrackList as TrackList; + +impl TrackList { + /// Whether this track list is an explicit track list (that is, doesn't have + /// any repeat values). + pub fn is_explicit(&self) -> bool { + !self.values.iter().any(|v| v.is_repeat()) + } + + /// Whether this track list has an `` value. + pub fn has_auto_repeat(&self) -> bool { + self.auto_repeat_index < self.values.len() + } +} + +impl ToCss for TrackList { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + let mut values_iter = self.values.iter().peekable(); + let mut line_names_iter = self.line_names.iter().peekable(); + + for idx in 0.. { + let names = line_names_iter.next().unwrap(); // This should exist! + concat_serialize_idents("[", "]", names, " ", dest)?; + + match values_iter.next() { + Some(value) => { + if !names.is_empty() { + dest.write_char(' ')?; + } + + value.to_css(dest)?; + }, + None => break, + } + + if values_iter.peek().is_some() || + line_names_iter.peek().map_or(false, |v| !v.is_empty()) || + (idx + 1 == self.auto_repeat_index) + { + dest.write_char(' ')?; + } + } + + Ok(()) + } +} + +/// The `` for subgrids. +/// +/// = repeat( [ | auto-fill ], +) +/// +/// https://drafts.csswg.org/css-grid/#typedef-name-repeat +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +pub struct GenericNameRepeat { + /// The number of times for the value to be repeated (could also be `auto-fill`). + /// Note: `RepeatCount` accepts `auto-fit`, so we should reject it after parsing it. + pub count: RepeatCount, + /// This represents `+`. The length of the outer vector is at least one. + pub line_names: crate::OwnedSlice>, +} + +pub use self::GenericNameRepeat as NameRepeat; + +impl ToCss for NameRepeat { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + dest.write_str("repeat(")?; + self.count.to_css(dest)?; + dest.write_char(',')?; + + for ref names in self.line_names.iter() { + if names.is_empty() { + // Note: concat_serialize_idents() skip the empty list so we have to handle it + // manually for NameRepeat. + dest.write_str(" []")?; + } else { + concat_serialize_idents(" [", "]", names, " ", dest)?; + } + } + + dest.write_char(')') + } +} + +impl NameRepeat { + /// Returns true if it is auto-fill. + #[inline] + pub fn is_auto_fill(&self) -> bool { + matches!(self.count, RepeatCount::AutoFill) + } +} + +/// A single value for `` or ``. +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[repr(C, u8)] +pub enum GenericLineNameListValue { + /// ``. + LineNames(crate::OwnedSlice), + /// ``. + Repeat(GenericNameRepeat), +} + +pub use self::GenericLineNameListValue as LineNameListValue; + +impl ToCss for LineNameListValue { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + match *self { + Self::Repeat(ref r) => r.to_css(dest), + Self::LineNames(ref names) => { + dest.write_char('[')?; + + if let Some((ref first, rest)) = names.split_first() { + first.to_css(dest)?; + for name in rest { + dest.write_char(' ')?; + name.to_css(dest)?; + } + } + + dest.write_char(']') + }, + } + } +} + +/// The `` for subgrids. +/// +/// = [ | ]+ +/// = repeat( [ | auto-fill ], +) +/// +/// https://drafts.csswg.org/css-grid/#typedef-line-name-list +#[derive( + Clone, + Debug, + Default, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +pub struct GenericLineNameList { + /// The pre-computed length of line_names, without the length of repeat(auto-fill, ...). + // We precomputed this at parsing time, so we can avoid an extra loop when expanding + // repeat(auto-fill). + pub expanded_line_names_length: usize, + /// The line name list. + pub line_names: crate::OwnedSlice>, +} + +pub use self::GenericLineNameList as LineNameList; + +impl ToCss for LineNameList { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + dest.write_str("subgrid")?; + + for value in self.line_names.iter() { + dest.write_char(' ')?; + value.to_css(dest)?; + } + + Ok(()) + } +} + +/// Variants for ` | ` +#[derive( + Animate, + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[value_info(other_values = "subgrid")] +#[repr(C, u8)] +pub enum GenericGridTemplateComponent { + /// `none` value. + None, + /// The grid `` + TrackList( + #[animation(field_bound)] + #[compute(field_bound)] + #[resolve(field_bound)] + #[shmem(field_bound)] + Box>, + ), + /// A `subgrid ?` + /// TODO: Support animations for this after subgrid is addressed in [grid-2] spec. + #[animation(error)] + Subgrid(Box>), + /// `masonry` value. + /// https://github.com/w3c/csswg-drafts/issues/4650 + Masonry, +} + +pub use self::GenericGridTemplateComponent as GridTemplateComponent; + +impl GridTemplateComponent { + /// The initial value. + const INITIAL_VALUE: Self = Self::None; + + /// Returns length of the s + pub fn track_list_len(&self) -> usize { + match *self { + GridTemplateComponent::TrackList(ref tracklist) => tracklist.values.len(), + _ => 0, + } + } + + /// Returns true if `self` is the initial value. + pub fn is_initial(&self) -> bool { + matches!(*self, Self::None) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585 + } +} + +impl Default for GridTemplateComponent { + #[inline] + fn default() -> Self { + Self::INITIAL_VALUE + } +} -- cgit v1.2.3