style/properties_and_values/syntax/
mod.rs1use std::fmt::{self, Debug};
10use std::{borrow::Cow, fmt::Write};
11
12use crate::derives::*;
13use crate::parser::{Parse, ParserContext};
14use crate::values::CustomIdent;
15use cssparser::{Parser as CSSParser, ParserInput as CSSParserInput, Token};
16use style_traits::{
17 CssWriter, ParseError as StyleParseError, PropertySyntaxParseError as ParseError,
18 StyleParseErrorKind, ToCss,
19};
20
21use self::data_type::{DataType, DependentDataTypes};
22
23mod ascii;
24pub mod data_type;
25
26#[derive(Debug, Clone, Default, MallocSizeOf, PartialEq, ToShmem)]
28pub struct Descriptor {
29 pub components: Vec<Component>,
32 specified: Option<Box<str>>,
34}
35
36impl Descriptor {
37 pub const fn universal() -> Self {
39 Self {
40 components: Vec::new(),
41 specified: None,
42 }
43 }
44
45 #[inline]
47 pub fn is_universal(&self) -> bool {
48 self.components.is_empty()
49 }
50
51 #[inline]
53 pub fn specified_string(&self) -> Option<&str> {
54 self.specified.as_deref()
55 }
56
57 #[inline]
60 pub fn from_css_parser<'i>(input: &mut CSSParser<'i, '_>) -> Result<Self, StyleParseError<'i>> {
61 let mut components = vec![];
63 loop {
64 let name = Self::try_parse_component_name(input).map_err(|err| {
65 input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(err))
66 })?;
67
68 let multiplier = if name.is_pre_multiplied() {
69 None
70 } else {
71 Self::try_parse_multiplier(input)
72 };
73
74 let component = Component { multiplier, name };
75 components.push(component);
76 let Ok(delim) = input.next() else { break };
77
78 if delim != &Token::Delim('|') {
79 return Err(
80 input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(
81 ParseError::ExpectedPipeBetweenComponents,
82 )),
83 );
84 }
85 }
86
87 Ok(Self {
88 components,
89 specified: None,
90 })
91 }
92
93 fn try_parse_multiplier<'i>(input: &mut CSSParser<'i, '_>) -> Option<Multiplier> {
94 input
95 .try_parse(|input| {
96 let next = input.next().map_err(|_| ())?;
97 match next {
98 Token::Delim('+') => Ok(Multiplier::Space),
99 Token::Delim('#') => Ok(Multiplier::Comma),
100 _ => Err(()),
101 }
102 })
103 .ok()
104 }
105
106 fn try_parse_component_name<'i>(
107 input: &mut CSSParser<'i, '_>,
108 ) -> Result<ComponentName, ParseError> {
109 if input.try_parse(|input| input.expect_delim('<')).is_ok() {
110 let name = Self::parse_component_data_type_name(input)?;
111 input
112 .expect_delim('>')
113 .map_err(|_| ParseError::UnclosedDataTypeName)?;
114 Ok(ComponentName::DataType(name))
115 } else {
116 input.try_parse(|input| {
117 let name = CustomIdent::parse(input, &[]).map_err(|_| ParseError::InvalidName)?;
118 Ok(ComponentName::Ident(name))
119 })
120 }
121 }
122
123 fn parse_component_data_type_name<'i>(
124 input: &mut CSSParser<'i, '_>,
125 ) -> Result<DataType, ParseError> {
126 input
127 .expect_ident()
128 .ok()
129 .and_then(|n| DataType::from_str(n))
130 .ok_or(ParseError::UnknownDataTypeName)
131 }
132
133 pub fn from_str(css: &str, save_specified: bool) -> Result<Self, ParseError> {
136 let input = ascii::trim_ascii_whitespace(css);
138
139 if input.is_empty() {
141 return Err(ParseError::EmptyInput);
142 }
143
144 let specified = if save_specified {
145 Some(Box::from(css))
146 } else {
147 None
148 };
149
150 if input.len() == 1 && input.as_bytes()[0] == b'*' {
153 return Ok(Self {
154 components: Default::default(),
155 specified,
156 });
157 }
158
159 let mut components = vec![];
166 {
167 let mut input = Parser::new(input, &mut components);
168 input.parse()?;
170 }
171 Ok(Self {
172 components,
173 specified,
174 })
175 }
176
177 pub fn dependent_types(&self) -> DependentDataTypes {
179 let mut types = DependentDataTypes::empty();
180 for component in self.components.iter() {
181 let t = match &component.name {
182 ComponentName::DataType(ref t) => t,
183 ComponentName::Ident(_) => continue,
184 };
185 types.insert(t.dependent_types());
186 }
187 types
188 }
189}
190
191impl ToCss for Descriptor {
192 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
193 where
194 W: Write,
195 {
196 if let Some(ref specified) = self.specified {
197 return specified.to_css(dest);
198 }
199
200 if self.is_universal() {
201 return dest.write_char('*');
202 }
203
204 let mut first = true;
205 for component in &*self.components {
206 if !first {
207 dest.write_str(" | ")?;
208 }
209 component.to_css(dest)?;
210 first = false;
211 }
212
213 Ok(())
214 }
215}
216
217impl Parse for Descriptor {
218 fn parse<'i>(
220 _: &ParserContext,
221 parser: &mut CSSParser<'i, '_>,
222 ) -> Result<Self, StyleParseError<'i>> {
223 let input = parser.expect_string()?;
224 Descriptor::from_str(input.as_ref(), true)
225 .map_err(|err| parser.new_custom_error(StyleParseErrorKind::PropertySyntaxField(err)))
226 }
227}
228
229#[derive(
231 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
232)]
233pub enum Multiplier {
234 Space,
236 Comma,
238}
239
240impl ToCss for Multiplier {
241 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
242 where
243 W: Write,
244 {
245 dest.write_char(match *self {
246 Multiplier::Space => '+',
247 Multiplier::Comma => '#',
248 })
249 }
250}
251
252#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
254pub struct Component {
255 name: ComponentName,
256 multiplier: Option<Multiplier>,
257}
258
259impl Component {
260 #[inline]
262 pub fn name(&self) -> &ComponentName {
263 &self.name
264 }
265
266 #[inline]
268 pub fn multiplier(&self) -> Option<Multiplier> {
269 self.multiplier
270 }
271
272 #[inline]
274 pub fn unpremultiplied(&self) -> Cow<'_, Self> {
275 match self.name.unpremultiply() {
276 Some(component) => {
277 debug_assert!(
278 self.multiplier.is_none(),
279 "Shouldn't have parsed a multiplier for a pre-multiplied data type name",
280 );
281 Cow::Owned(component)
282 },
283 None => Cow::Borrowed(self),
284 }
285 }
286}
287
288impl ToCss for Component {
289 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
290 where
291 W: Write,
292 {
293 self.name().to_css(dest)?;
294 self.multiplier().to_css(dest)
295 }
296}
297
298#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
300pub enum ComponentName {
301 DataType(DataType),
303 Ident(CustomIdent),
305}
306
307impl ComponentName {
308 fn unpremultiply(&self) -> Option<Component> {
309 match *self {
310 ComponentName::DataType(ref t) => t.unpremultiply(),
311 ComponentName::Ident(..) => None,
312 }
313 }
314
315 fn is_pre_multiplied(&self) -> bool {
317 self.unpremultiply().is_some()
318 }
319}
320
321struct Parser<'a> {
322 input: &'a str,
323 position: usize,
324 output: &'a mut Vec<Component>,
325}
326
327fn is_letter(byte: u8) -> bool {
329 match byte {
330 b'A'..=b'Z' | b'a'..=b'z' => true,
331 _ => false,
332 }
333}
334
335fn is_non_ascii(byte: u8) -> bool {
337 byte >= 0x80
338}
339
340fn is_name_start(byte: u8) -> bool {
342 is_letter(byte) || is_non_ascii(byte) || byte == b'_'
343}
344
345impl<'a> Parser<'a> {
346 fn new(input: &'a str, output: &'a mut Vec<Component>) -> Self {
347 Self {
348 input,
349 position: 0,
350 output,
351 }
352 }
353
354 fn peek(&self) -> Option<u8> {
355 self.input.as_bytes().get(self.position).cloned()
356 }
357
358 fn parse(&mut self) -> Result<(), ParseError> {
359 loop {
361 let component = self.parse_component()?;
362 self.output.push(component);
363 self.skip_whitespace();
364
365 let byte = match self.peek() {
366 None => return Ok(()),
367 Some(b) => b,
368 };
369
370 if byte != b'|' {
371 return Err(ParseError::ExpectedPipeBetweenComponents);
372 }
373
374 self.position += 1;
375 }
376 }
377
378 fn skip_whitespace(&mut self) {
379 loop {
380 match self.peek() {
381 Some(c) if c.is_ascii_whitespace() => self.position += 1,
382 _ => return,
383 }
384 }
385 }
386
387 fn parse_data_type_name(&mut self) -> Result<DataType, ParseError> {
389 let start = self.position;
390 loop {
391 let byte = match self.peek() {
392 Some(b) => b,
393 None => return Err(ParseError::UnclosedDataTypeName),
394 };
395 if byte != b'>' {
396 self.position += 1;
397 continue;
398 }
399 let ty = match DataType::from_str(&self.input[start..self.position]) {
400 Some(ty) => ty,
401 None => return Err(ParseError::UnknownDataTypeName),
402 };
403 self.position += 1;
404 return Ok(ty);
405 }
406 }
407
408 fn parse_name(&mut self) -> Result<ComponentName, ParseError> {
409 let b = match self.peek() {
410 Some(b) => b,
411 None => return Err(ParseError::UnexpectedEOF),
412 };
413
414 if b == b'<' {
415 self.position += 1;
416 return Ok(ComponentName::DataType(self.parse_data_type_name()?));
417 }
418
419 if b != b'\\' && !is_name_start(b) {
420 return Err(ParseError::InvalidNameStart);
421 }
422
423 let input = &self.input[self.position..];
424 let mut input = CSSParserInput::new(input);
425 let mut input = CSSParser::new(&mut input);
426 let name = match CustomIdent::parse(&mut input, &[]) {
427 Ok(name) => name,
428 Err(_) => return Err(ParseError::InvalidName),
429 };
430 self.position += input.position().byte_index();
431 return Ok(ComponentName::Ident(name));
432 }
433
434 fn parse_multiplier(&mut self) -> Option<Multiplier> {
435 let multiplier = match self.peek()? {
436 b'+' => Multiplier::Space,
437 b'#' => Multiplier::Comma,
438 _ => return None,
439 };
440 self.position += 1;
441 Some(multiplier)
442 }
443
444 fn parse_component(&mut self) -> Result<Component, ParseError> {
446 self.skip_whitespace();
448 let name = self.parse_name()?;
449 let multiplier = if name.is_pre_multiplied() {
450 None
451 } else {
452 self.parse_multiplier()
453 };
454 Ok(Component { name, multiplier })
455 }
456}