style/values/specified/
counters.rs1use crate::counter_style::CounterStyle;
8use crate::parser::{Parse, ParserContext};
9use crate::values::generics::counters as generics;
10use crate::values::generics::counters::CounterPair;
11use crate::values::specified::image::Image;
12use crate::values::specified::Integer;
13use crate::values::CustomIdent;
14use cssparser::{match_ignore_ascii_case, Parser, Token};
15use selectors::parser::SelectorParseErrorKind;
16use style_traits::{ParseError, StyleParseErrorKind};
17
18#[derive(PartialEq)]
19enum CounterType {
20 Increment,
21 Set,
22 Reset,
23}
24
25impl CounterType {
26 fn default_value(&self) -> i32 {
27 match *self {
28 Self::Increment => 1,
29 Self::Reset | Self::Set => 0,
30 }
31 }
32}
33
34pub type CounterIncrement = generics::GenericCounterIncrement<Integer>;
36
37impl Parse for CounterIncrement {
38 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
39 Ok(Self::new(parse_counters(
40 context,
41 input,
42 CounterType::Increment,
43 )?))
44 }
45}
46
47pub type CounterSet = generics::GenericCounterSet<Integer>;
49
50impl Parse for CounterSet {
51 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
52 Ok(Self::new(parse_counters(context, input, CounterType::Set)?))
53 }
54}
55
56pub type CounterReset = generics::GenericCounterReset<Integer>;
58
59impl Parse for CounterReset {
60 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
61 Ok(Self::new(parse_counters(
62 context,
63 input,
64 CounterType::Reset,
65 )?))
66 }
67}
68
69fn parse_counters(
70 context: &ParserContext,
71 input: &mut Parser,
72 counter_type: CounterType,
73) -> Result<Vec<CounterPair<Integer>>, ParseError> {
74 if input
75 .try_parse(|input| input.expect_ident_matching("none"))
76 .is_ok()
77 {
78 return Ok(vec![]);
79 }
80
81 let mut counters = Vec::new();
82 loop {
83 let (name, is_reversed) = match input.next() {
84 Ok(Token::Ident(ident)) => (CustomIdent::from_ident(ident, &["none"])?, false),
85 Ok(Token::Function(name))
86 if counter_type == CounterType::Reset && name.eq_ignore_ascii_case("reversed") =>
87 {
88 input
89 .parse_nested_block(|input| Ok((CustomIdent::parse(input, &["none"])?, true)))?
90 },
91 Ok(..) => {
92 return Err(ParseError::unexpected_token());
93 },
94 Err(_) => break,
95 };
96
97 let value = match input.try_parse(|input| Integer::parse(context, input)) {
98 Ok(start) => {
99 if start.get() == Some(i32::MIN) {
100 Integer::new(i32::MIN + 1)
104 } else {
105 start
106 }
107 },
108 _ => Integer::new(if is_reversed {
109 i32::MIN
110 } else {
111 counter_type.default_value()
112 }),
113 };
114 counters.push(CounterPair {
115 name,
116 value,
117 is_reversed,
118 });
119 }
120
121 if !counters.is_empty() {
122 Ok(counters)
123 } else {
124 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
125 }
126}
127
128pub type Content = generics::GenericContent<Image>;
130
131pub type ContentItem = generics::GenericContentItem<Image>;
133
134impl Content {
135 fn parse_counter_style(context: &ParserContext, input: &mut Parser) -> CounterStyle {
136 use crate::counter_style::CounterStyleParsingFlags;
137 input
138 .try_parse(|input| {
139 input.expect_comma()?;
140 CounterStyle::parse(context, input, CounterStyleParsingFlags::empty())
141 })
142 .unwrap_or_else(|_| CounterStyle::decimal())
143 }
144}
145
146impl Parse for Content {
147 #[cfg_attr(feature = "servo", allow(unused_mut))]
150 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
151 if input
152 .try_parse(|input| input.expect_ident_matching("normal"))
153 .is_ok()
154 {
155 return Ok(generics::Content::Normal);
156 }
157 if input
158 .try_parse(|input| input.expect_ident_matching("none"))
159 .is_ok()
160 {
161 return Ok(generics::Content::None);
162 }
163
164 let mut items = thin_vec::ThinVec::new();
165 let mut alt_start = None;
166 loop {
167 if alt_start.is_none() {
168 if let Ok(image) = input.try_parse(|i| Image::parse_forbid_none(context, i)) {
169 items.push(generics::ContentItem::Image(image));
170 continue;
171 }
172 }
173 let Ok(t) = input.next() else { break };
174 match *t {
175 Token::QuotedString(ref value) => {
176 items.push(generics::ContentItem::String(
177 value.as_ref().to_owned().into(),
178 ));
179 },
180 Token::Function(ref name) => {
181 let result = match_ignore_ascii_case! { &name,
185 "counter" if alt_start.is_none() => input.parse_nested_block(|input| {
186 let name = CustomIdent::parse(input, &[])?;
187 let style = Content::parse_counter_style(context, input);
188 Ok(generics::ContentItem::Counter(name, style))
189 }),
190 "counters" if alt_start.is_none() => input.parse_nested_block(|input| {
191 let name = CustomIdent::parse(input, &[])?;
192 input.expect_comma()?;
193 let separator = input.expect_string()?.as_ref().to_owned().into();
194 let style = Content::parse_counter_style(context, input);
195 Ok(generics::ContentItem::Counters(name, separator, style))
196 }),
197 _ => {
198 use style_traits::StyleParseErrorKind;
199 return Err(ParseError::custom(
200 StyleParseErrorKind::UnexpectedFunction,
201 ))
202 }
203 }?;
204 items.push(result);
205 },
206 Token::Ident(ref ident) if alt_start.is_none() => {
207 items.push(match_ignore_ascii_case! { &ident,
208 "open-quote" => generics::ContentItem::OpenQuote,
209 "close-quote" => generics::ContentItem::CloseQuote,
210 "no-open-quote" => generics::ContentItem::NoOpenQuote,
211 "no-close-quote" => generics::ContentItem::NoCloseQuote,
212 #[cfg(feature = "gecko")]
213 "-moz-alt-content" if context.in_ua_sheet() => {
214 generics::ContentItem::MozAltContent
215 },
216 #[cfg(feature = "gecko")]
217 "-moz-label-content" if context.chrome_rules_enabled() => {
218 generics::ContentItem::MozLabelContent
219 },
220 _ =>{
221 return Err(ParseError::custom(
222 SelectorParseErrorKind::UnexpectedIdent
223 ));
224 }
225 });
226 },
227 Token::Delim('/') if alt_start.is_none() && !items.is_empty() => {
228 alt_start = Some(items.len());
229 },
230 _ => return Err(ParseError::unexpected_token()),
231 }
232 }
233 if items.is_empty() {
234 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
235 }
236 let alt_start = alt_start.unwrap_or(items.len());
237 Ok(generics::Content::Items(generics::GenericContentItems {
238 items,
239 alt_start,
240 }))
241 }
242}