1use 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, StyleParseErrorKind, ToCss};
18use thin_vec::ThinVec;
19
20pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthPercentage>;
22
23impl Parse for BackgroundSize {
24 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
25 if let Ok(width) = input.try_parse(|i| NonNegativeLengthPercentageOrAuto::parse(context, i))
26 {
27 let height = input
28 .try_parse(|i| NonNegativeLengthPercentageOrAuto::parse(context, i))
29 .unwrap_or(NonNegativeLengthPercentageOrAuto::auto());
30 return Ok(GenericBackgroundSize::ExplicitSize { width, height });
31 }
32 Ok(try_match_ident_ignore_ascii_case! { input,
33 "cover" => GenericBackgroundSize::Cover,
34 "contain" => GenericBackgroundSize::Contain,
35 })
36 }
37}
38
39#[derive(
41 Clone,
42 Copy,
43 Debug,
44 Eq,
45 MallocSizeOf,
46 Parse,
47 PartialEq,
48 SpecifiedValueInfo,
49 ToComputedValue,
50 ToCss,
51 ToResolvedValue,
52 ToShmem,
53 ToTyped,
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#[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 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 ToTyped for BackgroundRepeat {
116 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
117 match (self.0, self.1) {
118 (BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {
119 dest.push(TypedValue::Keyword(KeywordValue(CssString::from(
120 "repeat-x",
121 ))));
122 Ok(())
123 },
124 (BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {
125 dest.push(TypedValue::Keyword(KeywordValue(CssString::from(
126 "repeat-y",
127 ))));
128 Ok(())
129 },
130 (horizontal, vertical) if horizontal == vertical => {
131 ToTyped::to_typed(&horizontal, dest)
132 },
133 _ => Err(()),
134 }
135 }
136}
137
138impl Parse for BackgroundRepeat {
139 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
140 let ident = input.expect_ident_cloned()?;
141
142 match_ignore_ascii_case! { &ident,
143 "repeat-x" => {
144 return Ok(BackgroundRepeat(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat));
145 },
146 "repeat-y" => {
147 return Ok(BackgroundRepeat(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat));
148 },
149 _ => {},
150 }
151
152 let horizontal = match BackgroundRepeatKeyword::from_ident(&ident) {
153 Ok(h) => h,
154 Err(()) => {
155 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent));
156 },
157 };
158
159 let vertical = input.try_parse(BackgroundRepeatKeyword::parse).ok();
160 Ok(BackgroundRepeat(horizontal, vertical.unwrap_or(horizontal)))
161 }
162}
163
164fn background_clip_border_area_enabled(context: &ParserContext) -> bool {
165 context.chrome_rules_enabled()
166 || crate::pref!("layout.css.background-clip.border-area.enabled")
167}
168
169#[allow(missing_docs)]
177#[derive(
178 Clone,
179 Copy,
180 Debug,
181 Eq,
182 MallocSizeOf,
183 Parse,
184 PartialEq,
185 SpecifiedValueInfo,
186 ToComputedValue,
187 ToCss,
188 ToResolvedValue,
189 ToShmem,
190 ToTyped,
191)]
192#[repr(u8)]
193pub enum BackgroundClip {
194 BorderBox,
195 PaddingBox,
196 ContentBox,
197 #[cfg(feature = "gecko")]
201 #[value_info(skip)]
202 FillBox,
203 #[cfg(feature = "gecko")]
204 #[value_info(skip)]
205 StrokeBox,
206 #[cfg(feature = "gecko")]
207 #[value_info(skip)]
208 ViewBox,
209 #[cfg(feature = "gecko")]
210 #[value_info(skip)]
211 NoClip,
212 #[cfg(feature = "gecko")]
214 Text,
215 #[parse(condition = "background_clip_border_area_enabled")]
216 #[value_info(skip)]
217 BorderArea,
218}
219
220bitflags! {
221 #[derive(Clone, Copy)]
223 struct ClipValidity: u8 {
224 const BACKGROUND = 1 << 0;
225 const MASK = 1 << 1;
226 const BOTH = Self::BACKGROUND.bits() | Self::MASK.bits();
227 }
228}
229
230impl BackgroundClip {
231 fn validity(&self) -> ClipValidity {
232 match *self {
233 Self::BorderBox => ClipValidity::BOTH,
234 Self::PaddingBox => ClipValidity::BOTH,
235 Self::ContentBox => ClipValidity::BOTH,
236 #[cfg(feature = "gecko")]
237 Self::FillBox => ClipValidity::MASK,
238 #[cfg(feature = "gecko")]
239 Self::StrokeBox => ClipValidity::MASK,
240 #[cfg(feature = "gecko")]
241 Self::ViewBox => ClipValidity::MASK,
242 #[cfg(feature = "gecko")]
243 Self::NoClip => ClipValidity::MASK,
244 #[cfg(feature = "gecko")]
245 Self::Text => ClipValidity::BACKGROUND,
246 Self::BorderArea => ClipValidity::BACKGROUND,
247 }
248 }
249
250 pub fn parse_for_background(
252 context: &ParserContext,
253 input: &mut Parser,
254 ) -> Result<Self, ParseError> {
255 let clip = Self::parse(context, input)?;
256 if !clip.validity().intersects(ClipValidity::BACKGROUND) {
257 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
258 }
259 Ok(clip)
260 }
261
262 pub fn parse_for_mask(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
264 let clip = Self::parse(context, input)?;
265 if !clip.validity().intersects(ClipValidity::MASK) {
266 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
267 }
268 Ok(clip)
269 }
270}