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::{Parser, match_ignore_ascii_case};
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() || crate::pref!("layout.css.background-clip.border-area.enabled")
166}
167
168#[allow(missing_docs)]
176#[derive(
177 Clone,
178 Copy,
179 Debug,
180 Eq,
181 MallocSizeOf,
182 Parse,
183 PartialEq,
184 SpecifiedValueInfo,
185 ToComputedValue,
186 ToCss,
187 ToResolvedValue,
188 ToShmem,
189 ToTyped,
190)]
191#[repr(u8)]
192pub enum BackgroundClip {
193 BorderBox,
194 PaddingBox,
195 ContentBox,
196 #[cfg(feature = "gecko")]
200 #[value_info(skip)]
201 FillBox,
202 #[cfg(feature = "gecko")]
203 #[value_info(skip)]
204 StrokeBox,
205 #[cfg(feature = "gecko")]
206 #[value_info(skip)]
207 ViewBox,
208 #[cfg(feature = "gecko")]
209 #[value_info(skip)]
210 NoClip,
211 #[cfg(feature = "gecko")]
213 Text,
214 #[parse(condition = "background_clip_border_area_enabled")]
215 #[value_info(skip)]
216 BorderArea,
217}
218
219bitflags! {
220 #[derive(Clone, Copy)]
222 struct ClipValidity: u8 {
223 const BACKGROUND = 1 << 0;
224 const MASK = 1 << 1;
225 const BOTH = Self::BACKGROUND.bits() | Self::MASK.bits();
226 }
227}
228
229impl BackgroundClip {
230 fn validity(&self) -> ClipValidity {
231 match *self {
232 Self::BorderBox => ClipValidity::BOTH,
233 Self::PaddingBox => ClipValidity::BOTH,
234 Self::ContentBox => ClipValidity::BOTH,
235 #[cfg(feature = "gecko")]
236 Self::FillBox => ClipValidity::MASK,
237 #[cfg(feature = "gecko")]
238 Self::StrokeBox => ClipValidity::MASK,
239 #[cfg(feature = "gecko")]
240 Self::ViewBox => ClipValidity::MASK,
241 #[cfg(feature = "gecko")]
242 Self::NoClip => ClipValidity::MASK,
243 #[cfg(feature = "gecko")]
244 Self::Text => ClipValidity::BACKGROUND,
245 Self::BorderArea => ClipValidity::BACKGROUND,
246 }
247 }
248
249 pub fn parse_for_background(
251 context: &ParserContext,
252 input: &mut Parser,
253 ) -> Result<Self, ParseError> {
254 let clip = Self::parse(context, input)?;
255 if !clip.validity().intersects(ClipValidity::BACKGROUND) {
256 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
257 }
258 Ok(clip)
259 }
260
261 pub fn parse_for_mask(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
263 let clip = Self::parse(context, input)?;
264 if !clip.validity().intersects(ClipValidity::MASK) {
265 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
266 }
267 Ok(clip)
268 }
269}
270
271#[allow(missing_docs)]
275#[derive(
276 Clone,
277 Copy,
278 Debug,
279 Deserialize,
280 Eq,
281 FromPrimitive,
282 Hash,
283 MallocSizeOf,
284 Parse,
285 PartialEq,
286 Serialize,
287 SpecifiedValueInfo,
288 ToComputedValue,
289 ToCss,
290 ToResolvedValue,
291 ToShmem,
292 ToTyped,
293)]
294#[repr(u8)]
295pub enum BackgroundOrigin {
296 BorderBox,
297 PaddingBox,
298 ContentBox,
299 #[cfg(feature = "gecko")]
301 #[value_info(skip)]
302 FillBox,
303 #[cfg(feature = "gecko")]
304 #[value_info(skip)]
305 StrokeBox,
306 #[cfg(feature = "gecko")]
307 #[value_info(skip)]
308 ViewBox,
309}
310
311impl BackgroundOrigin {
312 pub fn parse_for_background(
315 _context: &ParserContext,
316 input: &mut Parser,
317 ) -> Result<Self, ParseError> {
318 let origin = Self::parse(input)?;
319 #[cfg(feature = "gecko")]
320 if matches!(origin, Self::FillBox | Self::StrokeBox | Self::ViewBox) {
321 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
322 }
323 Ok(origin)
324 }
325}
326
327impl From<BackgroundOrigin> for BackgroundClip {
328 fn from(origin: BackgroundOrigin) -> Self {
329 match origin {
330 BackgroundOrigin::BorderBox => Self::BorderBox,
331 BackgroundOrigin::PaddingBox => Self::PaddingBox,
332 BackgroundOrigin::ContentBox => Self::ContentBox,
333 #[cfg(feature = "gecko")]
334 BackgroundOrigin::FillBox => Self::FillBox,
335 #[cfg(feature = "gecko")]
336 BackgroundOrigin::StrokeBox => Self::StrokeBox,
337 #[cfg(feature = "gecko")]
338 BackgroundOrigin::ViewBox => Self::ViewBox,
339 }
340 }
341}
342
343#[allow(missing_docs)]
345#[derive(
346 Clone,
347 Copy,
348 Debug,
349 Deserialize,
350 Eq,
351 FromPrimitive,
352 Hash,
353 MallocSizeOf,
354 Parse,
355 PartialEq,
356 Serialize,
357 SpecifiedValueInfo,
358 ToComputedValue,
359 ToCss,
360 ToResolvedValue,
361 ToShmem,
362 ToTyped,
363)]
364#[repr(u8)]
365pub enum ImageLayerAttachment {
366 Scroll,
367 Fixed,
368 #[cfg(feature = "gecko")]
369 Local,
370}