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<'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#[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#[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 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}
171
172fn background_clip_border_area_enabled(context: &ParserContext) -> bool {
173 context.chrome_rules_enabled()
174 || static_prefs::pref!("layout.css.background-clip.border-area.enabled")
175}
176
177#[allow(missing_docs)]
185#[derive(
186 Clone,
187 Copy,
188 Debug,
189 Eq,
190 MallocSizeOf,
191 Parse,
192 PartialEq,
193 SpecifiedValueInfo,
194 ToComputedValue,
195 ToCss,
196 ToResolvedValue,
197 ToShmem,
198 ToTyped,
199)]
200#[repr(u8)]
201pub enum BackgroundClip {
202 BorderBox,
203 PaddingBox,
204 ContentBox,
205 #[cfg(feature = "gecko")]
209 #[value_info(skip)]
210 FillBox,
211 #[cfg(feature = "gecko")]
212 #[value_info(skip)]
213 StrokeBox,
214 #[cfg(feature = "gecko")]
215 #[value_info(skip)]
216 ViewBox,
217 #[cfg(feature = "gecko")]
218 #[value_info(skip)]
219 NoClip,
220 #[cfg(feature = "gecko")]
222 Text,
223 #[parse(condition = "background_clip_border_area_enabled")]
224 #[value_info(skip)]
225 BorderArea,
226}
227
228bitflags! {
229 #[derive(Clone, Copy)]
231 struct ClipValidity: u8 {
232 const BACKGROUND = 1 << 0;
233 const MASK = 1 << 1;
234 const BOTH = Self::BACKGROUND.bits() | Self::MASK.bits();
235 }
236}
237
238impl BackgroundClip {
239 fn validity(&self) -> ClipValidity {
240 match *self {
241 Self::BorderBox => ClipValidity::BOTH,
242 Self::PaddingBox => ClipValidity::BOTH,
243 Self::ContentBox => ClipValidity::BOTH,
244 #[cfg(feature = "gecko")]
245 Self::FillBox => ClipValidity::MASK,
246 #[cfg(feature = "gecko")]
247 Self::StrokeBox => ClipValidity::MASK,
248 #[cfg(feature = "gecko")]
249 Self::ViewBox => ClipValidity::MASK,
250 #[cfg(feature = "gecko")]
251 Self::NoClip => ClipValidity::MASK,
252 #[cfg(feature = "gecko")]
253 Self::Text => ClipValidity::BACKGROUND,
254 Self::BorderArea => ClipValidity::BACKGROUND,
255 }
256 }
257
258 pub fn parse_for_background<'i>(
260 context: &ParserContext,
261 input: &mut Parser<'i, '_>,
262 ) -> Result<Self, ParseError<'i>> {
263 let clip = Self::parse(context, input)?;
264 if !clip.validity().intersects(ClipValidity::BACKGROUND) {
265 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
266 }
267 Ok(clip)
268 }
269
270 pub fn parse_for_mask<'i>(
272 context: &ParserContext,
273 input: &mut Parser<'i, '_>,
274 ) -> Result<Self, ParseError<'i>> {
275 let clip = Self::parse(context, input)?;
276 if !clip.validity().intersects(ClipValidity::MASK) {
277 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
278 }
279 Ok(clip)
280 }
281}