Skip to main content

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