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