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::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
21use crate::values::{computed, serialize_atom_name};
22use cssparser::{
23 BasicParseErrorKind, ParseErrorKind, Parser, ParserInput, RuleBodyParser, SourceLocation,
24};
25use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
26use servo_arc::Arc;
27use std::fmt::{self, Write};
28use style_traits::{
29 CssStringWriter, CssWriter, ParseError, PropertyInheritsParseError, PropertySyntaxParseError,
30 StyleParseErrorKind, ToCss,
31};
32use to_shmem::{SharedMemoryBuilder, ToShmem};
33
34pub use super::syntax::Descriptor as SyntaxDescriptor;
35pub use crate::properties::property::{DescriptorId, DescriptorParser, Descriptors};
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 = Descriptors::default();
48 let mut parser = DescriptorParser {
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(ref 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 if descriptors.inherits.is_none() {
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 descriptors,
132 url_data: context.url_data.clone(),
133 source_location,
134 })
135}
136
137#[allow(missing_docs)]
139pub enum PropertyRegistrationError {
140 NoInitialValue,
141 InvalidInitialValue,
142 InitialValueNotComputationallyIndependent,
143}
144
145impl PropertyRegistration {
146 pub fn size_of(&self, _: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
148 MallocSizeOf::size_of(self, ops)
149 }
150
151 pub fn compute_initial_value(
153 &self,
154 computed_context: &computed::Context,
155 ) -> Result<ComputedRegisteredValue, ()> {
156 let Some(ref initial) = self.descriptors.initial_value else {
157 return Err(());
158 };
159
160 if self.descriptors.is_universal() {
161 return Ok(ComputedRegisteredValue::universal(Arc::clone(initial)));
162 }
163
164 let mut input = ParserInput::new(initial.css_text());
165 let mut input = Parser::new(&mut input);
166 input.skip_whitespace();
167
168 match SpecifiedRegisteredValue::compute(
169 &mut input,
170 &self.descriptors,
171 None,
172 &self.url_data,
173 computed_context,
174 AllowComputationallyDependent::No,
175 ) {
176 Ok(computed) => Ok(computed),
177 Err(_) => Err(()),
178 }
179 }
180
181 pub fn validate_initial_value(
184 syntax: &SyntaxDescriptor,
185 initial_value: Option<&SpecifiedValue>,
186 ) -> Result<(), PropertyRegistrationError> {
187 use crate::properties::CSSWideKeyword;
188 if syntax.is_universal() && initial_value.is_none() {
192 return Ok(());
193 }
194
195 let Some(initial) = initial_value else {
200 return Err(PropertyRegistrationError::NoInitialValue);
201 };
202
203 if initial.has_references() {
206 return Err(PropertyRegistrationError::InitialValueNotComputationallyIndependent);
207 }
208
209 let mut input = ParserInput::new(initial.css_text());
210 let mut input = Parser::new(&mut input);
211 input.skip_whitespace();
212
213 if input.try_parse(CSSWideKeyword::parse).is_ok() {
215 return Err(PropertyRegistrationError::InitialValueNotComputationallyIndependent);
216 }
217
218 match SpecifiedRegisteredValue::parse(
219 &mut input,
220 syntax,
221 &initial.url_data,
222 None,
223 AllowComputationallyDependent::No,
224 ) {
225 Ok(_) => {},
226 Err(_) => return Err(PropertyRegistrationError::InvalidInitialValue),
227 }
228
229 Ok(())
230 }
231}
232
233impl ToCssWithGuard for PropertyRegistration {
234 fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
236 dest.write_str("@property ")?;
237 self.name.to_css(&mut CssWriter::new(dest))?;
238 dest.write_str(" { ")?;
239 self.descriptors.to_css(&mut CssWriter::new(dest))?;
240 dest.write_char('}')
241 }
242}
243
244impl ToShmem for PropertyRegistration {
245 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
246 Err(String::from(
247 "ToShmem failed for PropertyRule: cannot handle @property rules",
248 ))
249 }
250}
251
252#[derive(Clone, Debug, PartialEq, MallocSizeOf)]
254pub struct PropertyRuleName(pub CustomPropertyName);
255
256impl ToCss for PropertyRuleName {
257 fn to_css<W: Write>(&self, dest: &mut CssWriter<W>) -> fmt::Result {
258 dest.write_str("--")?;
259 serialize_atom_name(&self.0, dest)
260 }
261}
262
263#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
265pub enum Inherits {
266 True,
268 False,
270}
271
272impl Parse for Inherits {
273 fn parse<'i, 't>(
274 _context: &ParserContext,
275 input: &mut Parser<'i, 't>,
276 ) -> Result<Self, ParseError<'i>> {
277 let result: Result<Inherits, ParseError> = (|| {
280 try_match_ident_ignore_ascii_case! { input,
281 "true" => Ok(Inherits::True),
282 "false" => Ok(Inherits::False),
283 }
284 })();
285 if let Err(err) = result {
286 Err(ParseError {
287 kind: ParseErrorKind::Custom(StyleParseErrorKind::PropertyInheritsField(
288 PropertyInheritsParseError::InvalidInherits,
289 )),
290 location: err.location,
291 })
292 } else {
293 result
294 }
295 }
296}
297
298pub type InitialValue = Arc<SpecifiedValue>;
303
304impl Parse for InitialValue {
305 fn parse<'i, 't>(
306 context: &ParserContext,
307 input: &mut Parser<'i, 't>,
308 ) -> Result<Self, ParseError<'i>> {
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().map_or(true, |s| s.is_universal())
338 }
339}