style/properties_and_values/
rule.rs1use super::{
10 registry::{PropertyRegistration, PropertyRegistrationData},
11 syntax::Descriptor,
12 value::{
13 AllowComputationallyDependent, ComputedValue as ComputedRegisteredValue,
14 SpecifiedValue as SpecifiedRegisteredValue,
15 },
16};
17use crate::custom_properties::{Name as CustomPropertyName, SpecifiedValue};
18use crate::error_reporting::ContextualParseError;
19use crate::parser::{Parse, ParserContext};
20use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
21use crate::str::CssStringWriter;
22use crate::values::{computed, serialize_atom_name};
23use cssparser::{
24 AtRuleParser, BasicParseErrorKind, CowRcStr, DeclarationParser, ParseErrorKind, Parser,
25 ParserInput, ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation,
26};
27#[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
28use selectors::parser::SelectorParseErrorKind;
29use servo_arc::Arc;
30use std::fmt::{self, Write};
31use style_traits::{
32 CssWriter, ParseError, PropertyInheritsParseError, PropertySyntaxParseError,
33 StyleParseErrorKind, ToCss,
34};
35use to_shmem::{SharedMemoryBuilder, ToShmem};
36
37pub fn parse_property_block<'i, 't>(
42 context: &ParserContext,
43 input: &mut Parser<'i, 't>,
44 name: PropertyRuleName,
45 source_location: SourceLocation,
46) -> Result<PropertyRegistration, ParseError<'i>> {
47 let mut descriptors = PropertyDescriptors::default();
48 let mut parser = PropertyRuleParser {
49 context,
50 descriptors: &mut descriptors,
51 };
52 let mut iter = RuleBodyParser::new(input, &mut parser);
53 let mut syntax_err = None;
54 let mut inherits_err = None;
55 while let Some(declaration) = iter.next() {
56 if !context.error_reporting_enabled() {
57 continue;
58 }
59 if let Err((error, slice)) = declaration {
60 let location = error.location;
61 let error = match error.kind {
62 ParseErrorKind::Custom(StyleParseErrorKind::PropertySyntaxField(_)) => {
66 syntax_err = Some(error.clone());
67 ContextualParseError::UnsupportedValue(slice, error)
68 },
69
70 ParseErrorKind::Custom(StyleParseErrorKind::PropertyInheritsField(_)) => {
73 inherits_err = Some(error.clone());
74 ContextualParseError::UnsupportedValue(slice, error)
75 },
76
77 _ => ContextualParseError::UnsupportedPropertyDescriptor(slice, error),
80 };
81 context.log_css_error(location, error);
82 }
83 }
84
85 let Some(syntax) = descriptors.syntax else {
90 return Err(if let Some(err) = syntax_err {
91 err
92 } else {
93 let err = input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(
94 PropertySyntaxParseError::NoSyntax,
95 ));
96 context.log_css_error(
97 source_location,
98 ContextualParseError::UnsupportedValue("", err.clone()),
99 );
100 err
101 });
102 };
103
104 let Some(inherits) = descriptors.inherits else {
109 return Err(if let Some(err) = inherits_err {
110 err
111 } else {
112 let err = input.new_custom_error(StyleParseErrorKind::PropertyInheritsField(
113 PropertyInheritsParseError::NoInherits,
114 ));
115 context.log_css_error(
116 source_location,
117 ContextualParseError::UnsupportedValue("", err.clone()),
118 );
119 err
120 });
121 };
122
123 if PropertyRegistration::validate_initial_value(&syntax, descriptors.initial_value.as_deref())
124 .is_err()
125 {
126 return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid));
127 }
128
129 Ok(PropertyRegistration {
130 name,
131 data: PropertyRegistrationData {
132 syntax,
133 inherits,
134 initial_value: descriptors.initial_value,
135 },
136 url_data: context.url_data.clone(),
137 source_location,
138 })
139}
140
141struct PropertyRuleParser<'a, 'b: 'a> {
142 context: &'a ParserContext<'b>,
143 descriptors: &'a mut PropertyDescriptors,
144}
145
146impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyRuleParser<'a, 'b> {
148 type Prelude = ();
149 type AtRule = ();
150 type Error = StyleParseErrorKind<'i>;
151}
152
153impl<'a, 'b, 'i> QualifiedRuleParser<'i> for PropertyRuleParser<'a, 'b> {
154 type Prelude = ();
155 type QualifiedRule = ();
156 type Error = StyleParseErrorKind<'i>;
157}
158
159impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
160 for PropertyRuleParser<'a, 'b>
161{
162 fn parse_qualified(&self) -> bool {
163 false
164 }
165 fn parse_declarations(&self) -> bool {
166 true
167 }
168}
169
170macro_rules! property_descriptors {
171 (
172 $( #[$doc: meta] $name: tt $ident: ident: $ty: ty, )*
173 ) => {
174 #[derive(Clone, Debug, Default, PartialEq)]
178 struct PropertyDescriptors {
179 $(
180 #[$doc]
181 $ident: Option<$ty>,
182 )*
183 }
184
185 impl PropertyRegistration {
186 fn decl_to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
187 $(
188 let $ident = Option::<&$ty>::from(&self.data.$ident);
189 if let Some(ref value) = $ident {
190 dest.write_str(concat!($name, ": "))?;
191 value.to_css(&mut CssWriter::new(dest))?;
192 dest.write_str("; ")?;
193 }
194 )*
195 Ok(())
196 }
197 }
198
199 impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyRuleParser<'a, 'b> {
200 type Declaration = ();
201 type Error = StyleParseErrorKind<'i>;
202
203 fn parse_value<'t>(
204 &mut self,
205 name: CowRcStr<'i>,
206 input: &mut Parser<'i, 't>,
207 _declaration_start: &ParserState,
208 ) -> Result<(), ParseError<'i>> {
209 match_ignore_ascii_case! { &*name,
210 $(
211 $name => {
212 let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
216 self.descriptors.$ident = Some(value)
217 },
218 )*
219 _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
220 }
221 Ok(())
222 }
223 }
224 }
225}
226
227property_descriptors! {
228 "syntax" syntax: Descriptor,
230
231 "inherits" inherits: Inherits,
233
234 "initial-value" initial_value: InitialValue,
236}
237
238#[allow(missing_docs)]
240pub enum PropertyRegistrationError {
241 NoInitialValue,
242 InvalidInitialValue,
243 InitialValueNotComputationallyIndependent,
244}
245
246impl PropertyRegistration {
247 #[cfg(feature = "gecko")]
249 pub fn size_of(&self, _: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
250 MallocSizeOf::size_of(self, ops)
251 }
252
253 pub fn compute_initial_value(
255 &self,
256 computed_context: &computed::Context,
257 ) -> Result<ComputedRegisteredValue, ()> {
258 let Some(ref initial) = self.data.initial_value else {
259 return Err(());
260 };
261
262 if self.data.syntax.is_universal() {
263 return Ok(ComputedRegisteredValue::universal(Arc::clone(initial)));
264 }
265
266 let mut input = ParserInput::new(initial.css_text());
267 let mut input = Parser::new(&mut input);
268 input.skip_whitespace();
269
270 match SpecifiedRegisteredValue::compute(
271 &mut input,
272 &self.data,
273 &self.url_data,
274 computed_context,
275 AllowComputationallyDependent::No,
276 ) {
277 Ok(computed) => Ok(computed),
278 Err(_) => Err(()),
279 }
280 }
281
282 pub fn validate_initial_value(
285 syntax: &Descriptor,
286 initial_value: Option<&SpecifiedValue>,
287 ) -> Result<(), PropertyRegistrationError> {
288 use crate::properties::CSSWideKeyword;
289 if syntax.is_universal() && initial_value.is_none() {
293 return Ok(());
294 }
295
296 let Some(initial) = initial_value else {
301 return Err(PropertyRegistrationError::NoInitialValue);
302 };
303
304 if initial.has_references() {
307 return Err(PropertyRegistrationError::InitialValueNotComputationallyIndependent);
308 }
309
310 let mut input = ParserInput::new(initial.css_text());
311 let mut input = Parser::new(&mut input);
312 input.skip_whitespace();
313
314 if input.try_parse(CSSWideKeyword::parse).is_ok() {
316 return Err(PropertyRegistrationError::InitialValueNotComputationallyIndependent);
317 }
318
319 match SpecifiedRegisteredValue::parse(
320 &mut input,
321 syntax,
322 &initial.url_data,
323 AllowComputationallyDependent::No,
324 ) {
325 Ok(_) => {},
326 Err(_) => return Err(PropertyRegistrationError::InvalidInitialValue),
327 }
328
329 Ok(())
330 }
331}
332
333impl ToCssWithGuard for PropertyRegistration {
334 fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
336 dest.write_str("@property ")?;
337 self.name.to_css(&mut CssWriter::new(dest))?;
338 dest.write_str(" { ")?;
339 self.decl_to_css(dest)?;
340 dest.write_char('}')
341 }
342}
343
344impl ToShmem for PropertyRegistration {
345 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
346 Err(String::from(
347 "ToShmem failed for PropertyRule: cannot handle @property rules",
348 ))
349 }
350}
351
352#[derive(Clone, Debug, PartialEq, MallocSizeOf)]
354pub struct PropertyRuleName(pub CustomPropertyName);
355
356impl ToCss for PropertyRuleName {
357 fn to_css<W: Write>(&self, dest: &mut CssWriter<W>) -> fmt::Result {
358 dest.write_str("--")?;
359 serialize_atom_name(&self.0, dest)
360 }
361}
362
363#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
365pub enum Inherits {
366 True,
368 False,
370}
371
372impl Parse for Inherits {
373 fn parse<'i, 't>(
374 _context: &ParserContext,
375 input: &mut Parser<'i, 't>,
376 ) -> Result<Self, ParseError<'i>> {
377 let result: Result<Inherits, ParseError> = (|| {
380 try_match_ident_ignore_ascii_case! { input,
381 "true" => Ok(Inherits::True),
382 "false" => Ok(Inherits::False),
383 }
384 })();
385 if let Err(err) = result {
386 Err(ParseError {
387 kind: ParseErrorKind::Custom(StyleParseErrorKind::PropertyInheritsField(
388 PropertyInheritsParseError::InvalidInherits,
389 )),
390 location: err.location,
391 })
392 } else {
393 result
394 }
395 }
396}
397
398pub type InitialValue = Arc<SpecifiedValue>;
403
404impl Parse for InitialValue {
405 fn parse<'i, 't>(
406 context: &ParserContext,
407 input: &mut Parser<'i, 't>,
408 ) -> Result<Self, ParseError<'i>> {
409 input.skip_whitespace();
410 Ok(Arc::new(SpecifiedValue::parse(input, &context.url_data)?))
411 }
412}