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