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