stylo/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, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation,
26};
27#[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
28use selectors::parser::SelectorParseErrorKind;
29use servo_arc::Arc;
30use std::fmt::{self, Write};
31use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
32use to_shmem::{SharedMemoryBuilder, ToShmem};
33
34pub fn parse_property_block<'i, 't>(
39 context: &ParserContext,
40 input: &mut Parser<'i, 't>,
41 name: PropertyRuleName,
42 source_location: SourceLocation,
43) -> Result<PropertyRegistration, ParseError<'i>> {
44 let mut descriptors = PropertyDescriptors::default();
45 let mut parser = PropertyRuleParser {
46 context,
47 descriptors: &mut descriptors,
48 };
49 let mut iter = RuleBodyParser::new(input, &mut parser);
50 while let Some(declaration) = iter.next() {
51 if !context.error_reporting_enabled() {
52 continue;
53 }
54 if let Err((error, slice)) = declaration {
55 let location = error.location;
56 let error = if matches!(
57 error.kind,
58 ParseErrorKind::Custom(StyleParseErrorKind::PropertySyntaxField(_))
59 ) {
60 ContextualParseError::UnsupportedValue(slice, error)
64 } else {
65 ContextualParseError::UnsupportedPropertyDescriptor(slice, error)
68 };
69 context.log_css_error(location, error);
70 }
71 }
72
73 let Some(syntax) = descriptors.syntax else {
78 return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid));
79 };
80
81 let Some(inherits) = descriptors.inherits else {
86 return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid));
87 };
88
89 if PropertyRegistration::validate_initial_value(&syntax, descriptors.initial_value.as_deref())
90 .is_err()
91 {
92 return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid));
93 }
94
95 Ok(PropertyRegistration {
96 name,
97 data: PropertyRegistrationData {
98 syntax,
99 inherits,
100 initial_value: descriptors.initial_value,
101 },
102 url_data: context.url_data.clone(),
103 source_location,
104 })
105}
106
107struct PropertyRuleParser<'a, 'b: 'a> {
108 context: &'a ParserContext<'b>,
109 descriptors: &'a mut PropertyDescriptors,
110}
111
112impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyRuleParser<'a, 'b> {
114 type Prelude = ();
115 type AtRule = ();
116 type Error = StyleParseErrorKind<'i>;
117}
118
119impl<'a, 'b, 'i> QualifiedRuleParser<'i> for PropertyRuleParser<'a, 'b> {
120 type Prelude = ();
121 type QualifiedRule = ();
122 type Error = StyleParseErrorKind<'i>;
123}
124
125impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
126 for PropertyRuleParser<'a, 'b>
127{
128 fn parse_qualified(&self) -> bool {
129 false
130 }
131 fn parse_declarations(&self) -> bool {
132 true
133 }
134}
135
136macro_rules! property_descriptors {
137 (
138 $( #[$doc: meta] $name: tt $ident: ident: $ty: ty, )*
139 ) => {
140 #[derive(Clone, Debug, Default, PartialEq)]
144 struct PropertyDescriptors {
145 $(
146 #[$doc]
147 $ident: Option<$ty>,
148 )*
149 }
150
151 impl PropertyRegistration {
152 fn decl_to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
153 $(
154 let $ident = Option::<&$ty>::from(&self.data.$ident);
155 if let Some(ref value) = $ident {
156 dest.write_str(concat!($name, ": "))?;
157 value.to_css(&mut CssWriter::new(dest))?;
158 dest.write_str("; ")?;
159 }
160 )*
161 Ok(())
162 }
163 }
164
165 impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyRuleParser<'a, 'b> {
166 type Declaration = ();
167 type Error = StyleParseErrorKind<'i>;
168
169 fn parse_value<'t>(
170 &mut self,
171 name: CowRcStr<'i>,
172 input: &mut Parser<'i, 't>,
173 ) -> Result<(), ParseError<'i>> {
174 match_ignore_ascii_case! { &*name,
175 $(
176 $name => {
177 let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
181 self.descriptors.$ident = Some(value)
182 },
183 )*
184 _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
185 }
186 Ok(())
187 }
188 }
189 }
190}
191
192property_descriptors! {
193 "syntax" syntax: Descriptor,
195
196 "inherits" inherits: Inherits,
198
199 "initial-value" initial_value: InitialValue,
201}
202
203#[allow(missing_docs)]
205pub enum PropertyRegistrationError {
206 NoInitialValue,
207 InvalidInitialValue,
208 InitialValueNotComputationallyIndependent,
209}
210
211impl PropertyRegistration {
212 #[cfg(feature = "gecko")]
214 pub fn size_of(&self, _: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
215 MallocSizeOf::size_of(self, ops)
216 }
217
218 pub fn compute_initial_value(
220 &self,
221 computed_context: &computed::Context,
222 ) -> Result<ComputedRegisteredValue, ()> {
223 let Some(ref initial) = self.data.initial_value else {
224 return Err(());
225 };
226
227 if self.data.syntax.is_universal() {
228 return Ok(ComputedRegisteredValue::universal(Arc::clone(initial)));
229 }
230
231 let mut input = ParserInput::new(initial.css_text());
232 let mut input = Parser::new(&mut input);
233 input.skip_whitespace();
234
235 match SpecifiedRegisteredValue::compute(
236 &mut input,
237 &self.data,
238 &self.url_data,
239 computed_context,
240 AllowComputationallyDependent::No,
241 ) {
242 Ok(computed) => Ok(computed),
243 Err(_) => Err(()),
244 }
245 }
246
247 pub fn validate_initial_value(
250 syntax: &Descriptor,
251 initial_value: Option<&SpecifiedValue>,
252 ) -> Result<(), PropertyRegistrationError> {
253 use crate::properties::CSSWideKeyword;
254 if syntax.is_universal() && initial_value.is_none() {
258 return Ok(());
259 }
260
261 let Some(initial) = initial_value else {
266 return Err(PropertyRegistrationError::NoInitialValue);
267 };
268
269 if initial.has_references() {
272 return Err(PropertyRegistrationError::InitialValueNotComputationallyIndependent);
273 }
274
275 let mut input = ParserInput::new(initial.css_text());
276 let mut input = Parser::new(&mut input);
277 input.skip_whitespace();
278
279 if input.try_parse(CSSWideKeyword::parse).is_ok() {
281 return Err(PropertyRegistrationError::InitialValueNotComputationallyIndependent);
282 }
283
284 match SpecifiedRegisteredValue::parse(
285 &mut input,
286 syntax,
287 &initial.url_data,
288 AllowComputationallyDependent::No,
289 ) {
290 Ok(_) => {},
291 Err(_) => return Err(PropertyRegistrationError::InvalidInitialValue),
292 }
293
294 Ok(())
295 }
296}
297
298impl ToCssWithGuard for PropertyRegistration {
299 fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
301 dest.write_str("@property ")?;
302 self.name.to_css(&mut CssWriter::new(dest))?;
303 dest.write_str(" { ")?;
304 self.decl_to_css(dest)?;
305 dest.write_char('}')
306 }
307}
308
309impl ToShmem for PropertyRegistration {
310 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
311 Err(String::from(
312 "ToShmem failed for PropertyRule: cannot handle @property rules",
313 ))
314 }
315}
316
317#[derive(Clone, Debug, PartialEq, MallocSizeOf)]
319pub struct PropertyRuleName(pub CustomPropertyName);
320
321impl ToCss for PropertyRuleName {
322 fn to_css<W: Write>(&self, dest: &mut CssWriter<W>) -> fmt::Result {
323 dest.write_str("--")?;
324 serialize_atom_name(&self.0, dest)
325 }
326}
327
328#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, ToCss)]
330pub enum Inherits {
331 True,
333 False,
335}
336
337pub type InitialValue = Arc<SpecifiedValue>;
342
343impl Parse for InitialValue {
344 fn parse<'i, 't>(
345 context: &ParserContext,
346 input: &mut Parser<'i, 't>,
347 ) -> Result<Self, ParseError<'i>> {
348 input.skip_whitespace();
349 Ok(Arc::new(SpecifiedValue::parse(input, &context.url_data)?))
350 }
351}