style/values/specified/
background.rs

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//! Specified types for CSS values related to backgrounds.
6
7use crate::derives::*;
8use crate::parser::{Parse, ParserContext};
9use crate::values::generics::background::BackgroundSize as GenericBackgroundSize;
10use crate::values::specified::length::{
11    NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto,
12};
13use cssparser::{match_ignore_ascii_case, Parser};
14use selectors::parser::SelectorParseErrorKind;
15use std::fmt::{self, Write};
16use style_traits::{CssWriter, ParseError, ToCss};
17
18/// A specified value for the `background-size` property.
19pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthPercentage>;
20
21impl Parse for BackgroundSize {
22    fn parse<'i, 't>(
23        context: &ParserContext,
24        input: &mut Parser<'i, 't>,
25    ) -> Result<Self, ParseError<'i>> {
26        if let Ok(width) = input.try_parse(|i| NonNegativeLengthPercentageOrAuto::parse(context, i))
27        {
28            let height = input
29                .try_parse(|i| NonNegativeLengthPercentageOrAuto::parse(context, i))
30                .unwrap_or(NonNegativeLengthPercentageOrAuto::auto());
31            return Ok(GenericBackgroundSize::ExplicitSize { width, height });
32        }
33        Ok(try_match_ident_ignore_ascii_case! { input,
34            "cover" => GenericBackgroundSize::Cover,
35            "contain" => GenericBackgroundSize::Contain,
36        })
37    }
38}
39
40/// One of the keywords for `background-repeat`.
41#[derive(
42    Clone,
43    Copy,
44    Debug,
45    Eq,
46    MallocSizeOf,
47    Parse,
48    PartialEq,
49    SpecifiedValueInfo,
50    ToComputedValue,
51    ToCss,
52    ToResolvedValue,
53    ToShmem,
54)]
55#[allow(missing_docs)]
56#[value_info(other_values = "repeat-x,repeat-y")]
57pub enum BackgroundRepeatKeyword {
58    Repeat,
59    Space,
60    Round,
61    NoRepeat,
62}
63
64/// The value of the `background-repeat` property, with `repeat-x` / `repeat-y`
65/// represented as the combination of `no-repeat` and `repeat` in the opposite
66/// axes.
67///
68/// https://drafts.csswg.org/css-backgrounds/#the-background-repeat
69#[derive(
70    Clone,
71    Debug,
72    MallocSizeOf,
73    PartialEq,
74    SpecifiedValueInfo,
75    ToComputedValue,
76    ToResolvedValue,
77    ToShmem,
78)]
79pub struct BackgroundRepeat(pub BackgroundRepeatKeyword, pub BackgroundRepeatKeyword);
80
81impl BackgroundRepeat {
82    /// Returns the `repeat repeat` value.
83    pub fn repeat() -> Self {
84        BackgroundRepeat(
85            BackgroundRepeatKeyword::Repeat,
86            BackgroundRepeatKeyword::Repeat,
87        )
88    }
89}
90
91impl ToCss for BackgroundRepeat {
92    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
93    where
94        W: Write,
95    {
96        match (self.0, self.1) {
97            (BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {
98                dest.write_str("repeat-x")
99            },
100            (BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {
101                dest.write_str("repeat-y")
102            },
103            (horizontal, vertical) => {
104                horizontal.to_css(dest)?;
105                if horizontal != vertical {
106                    dest.write_char(' ')?;
107                    vertical.to_css(dest)?;
108                }
109                Ok(())
110            },
111        }
112    }
113}
114
115impl Parse for BackgroundRepeat {
116    fn parse<'i, 't>(
117        _context: &ParserContext,
118        input: &mut Parser<'i, 't>,
119    ) -> Result<Self, ParseError<'i>> {
120        let ident = input.expect_ident_cloned()?;
121
122        match_ignore_ascii_case! { &ident,
123            "repeat-x" => {
124                return Ok(BackgroundRepeat(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat));
125            },
126            "repeat-y" => {
127                return Ok(BackgroundRepeat(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat));
128            },
129            _ => {},
130        }
131
132        let horizontal = match BackgroundRepeatKeyword::from_ident(&ident) {
133            Ok(h) => h,
134            Err(()) => {
135                return Err(
136                    input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
137                );
138            },
139        };
140
141        let vertical = input.try_parse(BackgroundRepeatKeyword::parse).ok();
142        Ok(BackgroundRepeat(horizontal, vertical.unwrap_or(horizontal)))
143    }
144}