tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

grid.rs (16280B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 //! CSS handling for the computed value of
      6 //! [grids](https://drafts.csswg.org/css-grid/)
      7 
      8 use crate::derives::*;
      9 use crate::parser::{Parse, ParserContext};
     10 use crate::values::generics::grid::{GridTemplateComponent, ImplicitGridTracks, RepeatCount};
     11 use crate::values::generics::grid::{LineNameList, LineNameListValue, NameRepeat, TrackBreadth};
     12 use crate::values::generics::grid::{TrackList, TrackListValue, TrackRepeat, TrackSize};
     13 use crate::values::specified::{Integer, LengthPercentage};
     14 use crate::values::{CSSFloat, CustomIdent};
     15 use cssparser::{Parser, Token};
     16 use std::mem;
     17 use style_traits::{ParseError, StyleParseErrorKind};
     18 
     19 /// Parse a single flexible length.
     20 pub fn parse_flex<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CSSFloat, ParseError<'i>> {
     21    let location = input.current_source_location();
     22    match *input.next()? {
     23        Token::Dimension {
     24            value, ref unit, ..
     25        } if unit.eq_ignore_ascii_case("fr") && value.is_sign_positive() => Ok(value),
     26        ref t => Err(location.new_unexpected_token_error(t.clone())),
     27    }
     28 }
     29 
     30 impl<L> TrackBreadth<L> {
     31    fn parse_keyword<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
     32        #[derive(Parse)]
     33        enum TrackKeyword {
     34            Auto,
     35            MaxContent,
     36            MinContent,
     37        }
     38 
     39        Ok(match TrackKeyword::parse(input)? {
     40            TrackKeyword::Auto => TrackBreadth::Auto,
     41            TrackKeyword::MaxContent => TrackBreadth::MaxContent,
     42            TrackKeyword::MinContent => TrackBreadth::MinContent,
     43        })
     44    }
     45 }
     46 
     47 impl Parse for TrackBreadth<LengthPercentage> {
     48    fn parse<'i, 't>(
     49        context: &ParserContext,
     50        input: &mut Parser<'i, 't>,
     51    ) -> Result<Self, ParseError<'i>> {
     52        // FIXME: This and other callers in this file should use
     53        // NonNegativeLengthPercentage instead.
     54        //
     55        // Though it seems these cannot be animated so it's ~ok.
     56        if let Ok(lp) = input.try_parse(|i| LengthPercentage::parse_non_negative(context, i)) {
     57            return Ok(TrackBreadth::Breadth(lp));
     58        }
     59 
     60        if let Ok(f) = input.try_parse(parse_flex) {
     61            return Ok(TrackBreadth::Fr(f));
     62        }
     63 
     64        Self::parse_keyword(input)
     65    }
     66 }
     67 
     68 impl Parse for TrackSize<LengthPercentage> {
     69    fn parse<'i, 't>(
     70        context: &ParserContext,
     71        input: &mut Parser<'i, 't>,
     72    ) -> Result<Self, ParseError<'i>> {
     73        if let Ok(b) = input.try_parse(|i| TrackBreadth::parse(context, i)) {
     74            return Ok(TrackSize::Breadth(b));
     75        }
     76 
     77        if input
     78            .try_parse(|i| i.expect_function_matching("minmax"))
     79            .is_ok()
     80        {
     81            return input.parse_nested_block(|input| {
     82                let inflexible_breadth =
     83                    match input.try_parse(|i| LengthPercentage::parse_non_negative(context, i)) {
     84                        Ok(lp) => TrackBreadth::Breadth(lp),
     85                        Err(..) => TrackBreadth::parse_keyword(input)?,
     86                    };
     87 
     88                input.expect_comma()?;
     89                Ok(TrackSize::Minmax(
     90                    inflexible_breadth,
     91                    TrackBreadth::parse(context, input)?,
     92                ))
     93            });
     94        }
     95 
     96        input.expect_function_matching("fit-content")?;
     97        let lp = input.parse_nested_block(|i| LengthPercentage::parse_non_negative(context, i))?;
     98        Ok(TrackSize::FitContent(TrackBreadth::Breadth(lp)))
     99    }
    100 }
    101 
    102 impl Parse for ImplicitGridTracks<TrackSize<LengthPercentage>> {
    103    fn parse<'i, 't>(
    104        context: &ParserContext,
    105        input: &mut Parser<'i, 't>,
    106    ) -> Result<Self, ParseError<'i>> {
    107        use style_traits::{Separator, Space};
    108        let track_sizes = Space::parse(input, |i| TrackSize::parse(context, i))?;
    109        if track_sizes.len() == 1 && track_sizes[0].is_initial() {
    110            // A single track with the initial value is always represented by an empty slice.
    111            return Ok(Default::default());
    112        }
    113        return Ok(ImplicitGridTracks(track_sizes.into()));
    114    }
    115 }
    116 
    117 /// Parse the grid line names into a vector of owned strings.
    118 ///
    119 /// <https://drafts.csswg.org/css-grid/#typedef-line-names>
    120 pub fn parse_line_names<'i, 't>(
    121    input: &mut Parser<'i, 't>,
    122 ) -> Result<crate::OwnedSlice<CustomIdent>, ParseError<'i>> {
    123    input.expect_square_bracket_block()?;
    124    input.parse_nested_block(|input| {
    125        let mut values = vec![];
    126        while let Ok(ident) = input.try_parse(|i| CustomIdent::parse(i, &["span", "auto"])) {
    127            values.push(ident);
    128        }
    129 
    130        Ok(values.into())
    131    })
    132 }
    133 
    134 /// The type of `repeat` function (only used in parsing).
    135 ///
    136 /// <https://drafts.csswg.org/css-grid/#typedef-track-repeat>
    137 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo)]
    138 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
    139 enum RepeatType {
    140    /// [`<auto-repeat>`](https://drafts.csswg.org/css-grid/#typedef-auto-repeat)
    141    Auto,
    142    /// [`<track-repeat>`](https://drafts.csswg.org/css-grid/#typedef-track-repeat)
    143    Normal,
    144    /// [`<fixed-repeat>`](https://drafts.csswg.org/css-grid/#typedef-fixed-repeat)
    145    Fixed,
    146 }
    147 
    148 impl TrackRepeat<LengthPercentage, Integer> {
    149    fn parse_with_repeat_type<'i, 't>(
    150        context: &ParserContext,
    151        input: &mut Parser<'i, 't>,
    152    ) -> Result<(Self, RepeatType), ParseError<'i>> {
    153        input
    154            .try_parse(|i| i.expect_function_matching("repeat").map_err(|e| e.into()))
    155            .and_then(|_| {
    156                input.parse_nested_block(|input| {
    157                    let count = RepeatCount::parse(context, input)?;
    158                    input.expect_comma()?;
    159 
    160                    let is_auto = count == RepeatCount::AutoFit || count == RepeatCount::AutoFill;
    161                    let mut repeat_type = if is_auto {
    162                        RepeatType::Auto
    163                    } else {
    164                        // <fixed-size> is a subset of <track-size>, so it should work for both
    165                        RepeatType::Fixed
    166                    };
    167 
    168                    let mut names = vec![];
    169                    let mut values = vec![];
    170                    let mut current_names;
    171 
    172                    loop {
    173                        current_names = input.try_parse(parse_line_names).unwrap_or_default();
    174                        if let Ok(track_size) = input.try_parse(|i| TrackSize::parse(context, i)) {
    175                            if !track_size.is_fixed() {
    176                                if is_auto {
    177                                    // should be <fixed-size> for <auto-repeat>
    178                                    return Err(input
    179                                        .new_custom_error(StyleParseErrorKind::UnspecifiedError));
    180                                }
    181 
    182                                if repeat_type == RepeatType::Fixed {
    183                                    repeat_type = RepeatType::Normal // <track-size> for sure
    184                                }
    185                            }
    186 
    187                            values.push(track_size);
    188                            names.push(current_names);
    189                        } else {
    190                            if values.is_empty() {
    191                                // expecting at least one <track-size>
    192                                return Err(
    193                                    input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
    194                                );
    195                            }
    196 
    197                            names.push(current_names); // final `<line-names>`
    198                            break; // no more <track-size>, breaking
    199                        }
    200                    }
    201 
    202                    let repeat = TrackRepeat {
    203                        count,
    204                        track_sizes: values.into(),
    205                        line_names: names.into(),
    206                    };
    207 
    208                    Ok((repeat, repeat_type))
    209                })
    210            })
    211    }
    212 }
    213 
    214 impl Parse for TrackList<LengthPercentage, Integer> {
    215    fn parse<'i, 't>(
    216        context: &ParserContext,
    217        input: &mut Parser<'i, 't>,
    218    ) -> Result<Self, ParseError<'i>> {
    219        let mut current_names = vec![];
    220        let mut names = vec![];
    221        let mut values = vec![];
    222 
    223        // Whether we've parsed an `<auto-repeat>` value.
    224        let mut auto_repeat_index = None;
    225        // assume that everything is <fixed-size>. This flag is useful when we encounter <auto-repeat>
    226        let mut at_least_one_not_fixed = false;
    227        loop {
    228            current_names
    229                .extend_from_slice(&mut input.try_parse(parse_line_names).unwrap_or_default());
    230            if let Ok(track_size) = input.try_parse(|i| TrackSize::parse(context, i)) {
    231                if !track_size.is_fixed() {
    232                    at_least_one_not_fixed = true;
    233                    if auto_repeat_index.is_some() {
    234                        // <auto-track-list> only accepts <fixed-size> and <fixed-repeat>
    235                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    236                    }
    237                }
    238 
    239                let vec = mem::replace(&mut current_names, vec![]);
    240                names.push(vec.into());
    241                values.push(TrackListValue::TrackSize(track_size));
    242            } else if let Ok((repeat, type_)) =
    243                input.try_parse(|i| TrackRepeat::parse_with_repeat_type(context, i))
    244            {
    245                match type_ {
    246                    RepeatType::Normal => {
    247                        at_least_one_not_fixed = true;
    248                        if auto_repeat_index.is_some() {
    249                            // only <fixed-repeat>
    250                            return Err(
    251                                input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
    252                            );
    253                        }
    254                    },
    255                    RepeatType::Auto => {
    256                        if auto_repeat_index.is_some() || at_least_one_not_fixed {
    257                            // We've either seen <auto-repeat> earlier, or there's at least one non-fixed value
    258                            return Err(
    259                                input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
    260                            );
    261                        }
    262                        auto_repeat_index = Some(values.len());
    263                    },
    264                    RepeatType::Fixed => {},
    265                }
    266 
    267                let vec = mem::replace(&mut current_names, vec![]);
    268                names.push(vec.into());
    269                values.push(TrackListValue::TrackRepeat(repeat));
    270            } else {
    271                if values.is_empty() && auto_repeat_index.is_none() {
    272                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    273                }
    274 
    275                names.push(current_names.into());
    276                break;
    277            }
    278        }
    279 
    280        Ok(TrackList {
    281            auto_repeat_index: auto_repeat_index.unwrap_or(std::usize::MAX),
    282            values: values.into(),
    283            line_names: names.into(),
    284        })
    285    }
    286 }
    287 
    288 #[cfg(feature = "gecko")]
    289 #[inline]
    290 fn allow_grid_template_subgrids() -> bool {
    291    true
    292 }
    293 
    294 #[cfg(feature = "servo")]
    295 #[inline]
    296 fn allow_grid_template_subgrids() -> bool {
    297    false
    298 }
    299 
    300 #[cfg(feature = "gecko")]
    301 #[inline]
    302 fn allow_grid_template_masonry() -> bool {
    303    static_prefs::pref!("layout.css.grid-template-masonry-value.enabled")
    304 }
    305 
    306 #[cfg(feature = "servo")]
    307 #[inline]
    308 fn allow_grid_template_masonry() -> bool {
    309    false
    310 }
    311 
    312 impl Parse for GridTemplateComponent<LengthPercentage, Integer> {
    313    fn parse<'i, 't>(
    314        context: &ParserContext,
    315        input: &mut Parser<'i, 't>,
    316    ) -> Result<Self, ParseError<'i>> {
    317        if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
    318            return Ok(GridTemplateComponent::None);
    319        }
    320 
    321        Self::parse_without_none(context, input)
    322    }
    323 }
    324 
    325 impl GridTemplateComponent<LengthPercentage, Integer> {
    326    /// Parses a `GridTemplateComponent<LengthPercentage>` except `none` keyword.
    327    pub fn parse_without_none<'i, 't>(
    328        context: &ParserContext,
    329        input: &mut Parser<'i, 't>,
    330    ) -> Result<Self, ParseError<'i>> {
    331        if allow_grid_template_subgrids() {
    332            if let Ok(t) = input.try_parse(|i| LineNameList::parse(context, i)) {
    333                return Ok(GridTemplateComponent::Subgrid(Box::new(t)));
    334            }
    335        }
    336        if allow_grid_template_masonry() {
    337            if input
    338                .try_parse(|i| i.expect_ident_matching("masonry"))
    339                .is_ok()
    340            {
    341                return Ok(GridTemplateComponent::Masonry);
    342            }
    343        }
    344        let track_list = TrackList::parse(context, input)?;
    345        Ok(GridTemplateComponent::TrackList(Box::new(track_list)))
    346    }
    347 }
    348 
    349 impl Parse for NameRepeat<Integer> {
    350    fn parse<'i, 't>(
    351        context: &ParserContext,
    352        input: &mut Parser<'i, 't>,
    353    ) -> Result<Self, ParseError<'i>> {
    354        input.expect_function_matching("repeat")?;
    355        input.parse_nested_block(|i| {
    356            let count = RepeatCount::parse(context, i)?;
    357            // NameRepeat doesn't accept `auto-fit`
    358            // https://drafts.csswg.org/css-grid/#typedef-name-repeat
    359            if matches!(count, RepeatCount::AutoFit) {
    360                return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    361            }
    362 
    363            i.expect_comma()?;
    364            let mut names_list = vec![];
    365            names_list.push(parse_line_names(i)?); // there should be at least one
    366            while let Ok(names) = i.try_parse(parse_line_names) {
    367                names_list.push(names);
    368            }
    369 
    370            Ok(NameRepeat {
    371                count,
    372                line_names: names_list.into(),
    373            })
    374        })
    375    }
    376 }
    377 
    378 impl Parse for LineNameListValue<Integer> {
    379    fn parse<'i, 't>(
    380        context: &ParserContext,
    381        input: &mut Parser<'i, 't>,
    382    ) -> Result<Self, ParseError<'i>> {
    383        if let Ok(repeat) = input.try_parse(|i| NameRepeat::parse(context, i)) {
    384            return Ok(LineNameListValue::Repeat(repeat));
    385        }
    386 
    387        parse_line_names(input).map(LineNameListValue::LineNames)
    388    }
    389 }
    390 
    391 impl LineNameListValue<Integer> {
    392    /// Returns the length of `<line-names>` after expanding repeat(N, ...). This returns zero for
    393    /// repeat(auto-fill, ...).
    394    #[inline]
    395    pub fn line_names_length(&self) -> usize {
    396        match *self {
    397            Self::LineNames(..) => 1,
    398            Self::Repeat(ref r) => {
    399                match r.count {
    400                    // Note: RepeatCount is always >= 1.
    401                    RepeatCount::Number(v) => r.line_names.len() * v.value() as usize,
    402                    _ => 0,
    403                }
    404            },
    405        }
    406    }
    407 }
    408 
    409 impl Parse for LineNameList<Integer> {
    410    fn parse<'i, 't>(
    411        context: &ParserContext,
    412        input: &mut Parser<'i, 't>,
    413    ) -> Result<Self, ParseError<'i>> {
    414        input.expect_ident_matching("subgrid")?;
    415 
    416        let mut auto_repeat = false;
    417        let mut expanded_line_names_length = 0;
    418        let mut line_names = vec![];
    419        while let Ok(value) = input.try_parse(|i| LineNameListValue::parse(context, i)) {
    420            match value {
    421                LineNameListValue::Repeat(ref r) if r.is_auto_fill() => {
    422                    if auto_repeat {
    423                        // On a subgridded axis, the auto-fill keyword is only valid once per
    424                        // <line-name-list>.
    425                        // https://drafts.csswg.org/css-grid/#auto-repeat
    426                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    427                    }
    428                    auto_repeat = true;
    429                },
    430                _ => (),
    431            };
    432 
    433            expanded_line_names_length += value.line_names_length();
    434            line_names.push(value);
    435        }
    436 
    437        Ok(LineNameList {
    438            expanded_line_names_length,
    439            line_names: line_names.into(),
    440        })
    441    }
    442 }