style/properties_and_values/
rule.rs1use super::{
10 registry::PropertyRegistration,
11 value::{
12 AllowComputationallyDependent, ComputedValue as ComputedRegisteredValue,
13 SpecifiedValue as SpecifiedRegisteredValue,
14 },
15};
16use crate::custom_properties::{Name as CustomPropertyName, SpecifiedValue};
17use crate::derives::*;
18use crate::error_reporting::ContextualParseError;
19use crate::parser::{Parse, ParserContext};
20use crate::properties::PropertyIdRef;
21use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
22use crate::values::{computed, serialize_atom_name};
23use cssparser::{BasicParseErrorKind, ParseErrorKind, Parser, RuleBodyParser, SourceLocation};
24use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
25use servo_arc::Arc;
26use std::fmt::{self, Write};
27use style_traits::{
28 CssStringWriter, CssWriter, ParseError, PropertyInheritsParseError, PropertySyntaxParseError,
29 StyleParseErrorKind, ToCss,
30};
31use to_shmem::{SharedMemoryBuilder, ToShmem};
32
33pub use super::syntax::Descriptor as SyntaxDescriptor;
34pub use crate::properties::property::{DescriptorId, DescriptorParser, Descriptors};
35
36pub fn parse_property_block(
41 context: &ParserContext,
42 input: &mut Parser,
43 name: PropertyRuleName,
44 source_location: SourceLocation,
45) -> Result<PropertyRegistration, ParseError> {
46 let mut descriptors = Descriptors::default();
47 let mut parser = DescriptorParser {
48 context,
49 descriptors: &mut descriptors,
50 };
51 let iter = RuleBodyParser::new(input, &mut parser);
52 let mut syntax_err = None;
53 let mut inherits_err = None;
54 for declaration in iter {
55 if !context.error_reporting_enabled() {
56 continue;
57 }
58 if let Err((error, slice, location)) = declaration {
59 let error = match error.kind {
60 ParseErrorKind::Custom(StyleParseErrorKind::PropertySyntaxField(_)) => {
64 syntax_err = Some(error.clone());
65 ContextualParseError::UnsupportedValue(slice, error)
66 },
67
68 ParseErrorKind::Custom(StyleParseErrorKind::PropertyInheritsField(_)) => {
71 inherits_err = Some(error.clone());
72 ContextualParseError::UnsupportedValue(slice, error)
73 },
74
75 _ => ContextualParseError::UnsupportedPropertyDescriptor(slice, error),
78 };
79 context.log_css_error(location, error);
80 }
81 }
82
83 let Some(ref syntax) = descriptors.syntax else {
88 return Err(if let Some(err) = syntax_err {
89 err
90 } else {
91 let err = ParseError::custom(StyleParseErrorKind::PropertySyntaxField(
92 PropertySyntaxParseError::NoSyntax,
93 ));
94 context.log_css_error(
95 source_location,
96 ContextualParseError::UnsupportedValue("", err.clone()),
97 );
98 err
99 });
100 };
101
102 if descriptors.inherits.is_none() {
107 return Err(if let Some(err) = inherits_err {
108 err
109 } else {
110 let err = ParseError::custom(StyleParseErrorKind::PropertyInheritsField(
111 PropertyInheritsParseError::NoInherits,
112 ));
113 context.log_css_error(
114 source_location,
115 ContextualParseError::UnsupportedValue("", err.clone()),
116 );
117 err
118 });
119 };
120
121 if PropertyRegistration::validate_initial_value(
122 PropertyIdRef::from(&name.0),
123 syntax,
124 descriptors.initial_value.as_deref(),
125 )
126 .is_err()
127 {
128 return Err(ParseError::from_basic_kind(
129 BasicParseErrorKind::AtRuleBodyInvalid,
130 ));
131 }
132
133 Ok(PropertyRegistration {
134 name,
135 descriptors,
136 url_data: context.url_data.clone(),
137 source_location,
138 })
139}
140
141#[allow(missing_docs)]
143pub enum PropertyRegistrationError {
144 NoInitialValue,
145 InvalidInitialValue,
146 InitialValueNotComputationallyIndependent,
147}
148
149impl PropertyRegistration {
150 pub fn size_of(&self, _: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
152 MallocSizeOf::size_of(self, ops)
153 }
154
155 pub fn compute_initial_value(
157 &self,
158 computed_context: &computed::Context,
159 ) -> Result<ComputedRegisteredValue, ()> {
160 let Some(ref initial) = self.descriptors.initial_value else {
161 return Err(());
162 };
163
164 if self.descriptors.is_universal() {
165 return Ok(ComputedRegisteredValue::universal(Arc::clone(initial)));
166 }
167
168 let mut input = Parser::new(initial.css_text());
169 input.skip_whitespace();
170
171 match SpecifiedRegisteredValue::compute(
172 &mut input,
173 &self.descriptors,
174 None,
175 &self.url_data,
176 computed_context,
177 AllowComputationallyDependent::No,
178 Default::default(),
179 Some(PropertyIdRef::Custom(&self.name.0)),
180 ) {
181 Ok(computed) => Ok(computed),
182 Err(_) => Err(()),
183 }
184 }
185
186 pub fn validate_initial_value(
189 property_id: PropertyIdRef,
190 syntax: &SyntaxDescriptor,
191 initial_value: Option<&SpecifiedValue>,
192 ) -> Result<(), PropertyRegistrationError> {
193 use crate::properties::CSSWideKeyword;
194 if syntax.is_universal() && initial_value.is_none() {
198 return Ok(());
199 }
200
201 let Some(initial) = initial_value else {
206 return Err(PropertyRegistrationError::NoInitialValue);
207 };
208
209 if initial.has_references() {
212 return Err(PropertyRegistrationError::InitialValueNotComputationallyIndependent);
213 }
214
215 let mut input = Parser::new(initial.css_text());
216 input.skip_whitespace();
217
218 if input.try_parse(CSSWideKeyword::parse).is_ok() {
220 return Err(PropertyRegistrationError::InitialValueNotComputationallyIndependent);
221 }
222
223 match SpecifiedRegisteredValue::parse(
224 &mut input,
225 syntax,
226 &initial.url_data,
227 None,
228 AllowComputationallyDependent::No,
229 Default::default(),
230 Some(property_id),
231 ) {
232 Ok(_) => {},
233 Err(_) => return Err(PropertyRegistrationError::InvalidInitialValue),
234 }
235
236 Ok(())
237 }
238}
239
240impl ToCssWithGuard for PropertyRegistration {
241 fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
243 dest.write_str("@property ")?;
244 self.name.to_css(&mut CssWriter::new(dest))?;
245 dest.write_str(" { ")?;
246 self.descriptors.to_css(&mut CssWriter::new(dest))?;
247 dest.write_char('}')
248 }
249}
250
251impl ToShmem for PropertyRegistration {
252 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
253 Err(String::from(
254 "ToShmem failed for PropertyRule: cannot handle @property rules",
255 ))
256 }
257}
258
259#[derive(Clone, Debug, PartialEq, MallocSizeOf)]
261pub struct PropertyRuleName(pub CustomPropertyName);
262
263impl ToCss for PropertyRuleName {
264 fn to_css<W: Write>(&self, dest: &mut CssWriter<W>) -> fmt::Result {
265 dest.write_str("--")?;
266 serialize_atom_name(&self.0, dest)
267 }
268}
269
270#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
272pub enum Inherits {
273 True,
275 False,
277}
278
279impl Parse for Inherits {
280 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
281 let result: Result<Inherits, ParseError> = (|| {
284 try_match_ident_ignore_ascii_case! { input,
285 "true" => Ok(Inherits::True),
286 "false" => Ok(Inherits::False),
287 }
288 })();
289 if result.is_err() {
290 Err(ParseError::custom(
291 StyleParseErrorKind::PropertyInheritsField(
292 PropertyInheritsParseError::InvalidInherits,
293 ),
294 ))
295 } else {
296 result
297 }
298 }
299}
300
301pub type InitialValue = Arc<SpecifiedValue>;
306
307impl Parse for InitialValue {
308 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
309 input.skip_whitespace();
310 Ok(Arc::new(SpecifiedValue::parse(
311 input,
312 Some(&context.namespaces.prefixes),
313 context.url_data,
314 )?))
315 }
316}
317
318impl Descriptors {
319 #[inline]
321 pub fn unregistered() -> &'static Self {
322 static UNREGISTERED: Descriptors = Descriptors {
323 inherits: Some(Inherits::True),
324 syntax: Some(SyntaxDescriptor::universal()),
325 initial_value: None,
326 };
327 &UNREGISTERED
328 }
329
330 pub fn inherits(&self) -> bool {
332 self.inherits != Some(Inherits::False)
333 }
334
335 pub fn is_universal(&self) -> bool {
337 self.syntax.as_ref().is_none_or(|s| s.is_universal())
338 }
339}