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