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