summaryrefslogtreecommitdiffstats
path: root/servo/components/style/properties/helpers.mako.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /servo/components/style/properties/helpers.mako.rs
parentInitial commit. (diff)
downloadfirefox-upstream/124.0.1.tar.xz
firefox-upstream/124.0.1.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'servo/components/style/properties/helpers.mako.rs')
-rw-r--r--servo/components/style/properties/helpers.mako.rs909
1 files changed, 909 insertions, 0 deletions
diff --git a/servo/components/style/properties/helpers.mako.rs b/servo/components/style/properties/helpers.mako.rs
new file mode 100644
index 0000000000..968a97aa00
--- /dev/null
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -0,0 +1,909 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+<%!
+ from data import Keyword, to_rust_ident, to_phys, to_camel_case, SYSTEM_FONT_LONGHANDS
+ from data import (LOGICAL_CORNERS, PHYSICAL_CORNERS, LOGICAL_SIDES,
+ PHYSICAL_SIDES, LOGICAL_SIZES, LOGICAL_AXES)
+%>
+
+<%def name="predefined_type(name, type, initial_value, parse_method='parse',
+ vector=False, none_value=None, initial_specified_value=None,
+ allow_quirks='No', **kwargs)">
+ <%def name="predefined_type_inner(name, type, initial_value, parse_method)">
+ #[allow(unused_imports)]
+ use app_units::Au;
+ #[allow(unused_imports)]
+ use crate::values::specified::AllowQuirks;
+ #[allow(unused_imports)]
+ use crate::Zero;
+ #[allow(unused_imports)]
+ use smallvec::SmallVec;
+ pub use crate::values::specified::${type} as SpecifiedValue;
+ pub mod computed_value {
+ pub use crate::values::computed::${type} as T;
+ }
+ % if initial_value:
+ #[inline] pub fn get_initial_value() -> computed_value::T { ${initial_value} }
+ % endif
+ % if initial_specified_value:
+ #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { ${initial_specified_value} }
+ % endif
+ #[allow(unused_variables)]
+ #[inline]
+ pub fn parse<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<SpecifiedValue, ParseError<'i>> {
+ % if allow_quirks != "No":
+ specified::${type}::${parse_method}_quirky(context, input, AllowQuirks::${allow_quirks})
+ % elif parse_method != "parse":
+ specified::${type}::${parse_method}(context, input)
+ % else:
+ <specified::${type} as crate::parser::Parse>::parse(context, input)
+ % endif
+ }
+ </%def>
+ % if vector:
+ <%call
+ expr="vector_longhand(name, predefined_type=type, allow_empty=not initial_value, none_value=none_value, **kwargs)"
+ >
+ ${predefined_type_inner(name, type, initial_value, parse_method)}
+ % if caller:
+ ${caller.body()}
+ % endif
+ </%call>
+ % else:
+ <%call expr="longhand(name, predefined_type=type, **kwargs)">
+ ${predefined_type_inner(name, type, initial_value, parse_method)}
+ % if caller:
+ ${caller.body()}
+ % endif
+ </%call>
+ % endif
+</%def>
+
+// The setup here is roughly:
+//
+// * UnderlyingList is the list that is stored in the computed value. This may
+// be a shared ArcSlice if the property is inherited.
+// * UnderlyingOwnedList is the list that is used for animation.
+// * Specified values always use OwnedSlice, since it's more compact.
+// * computed_value::List is just a convenient alias that you can use for the
+// computed value list, since this is in the computed_value module.
+//
+// If simple_vector_bindings is true, then we don't use the complex iterator
+// machinery and set_foo_from, and just compute the value like any other
+// longhand.
+<%def name="vector_longhand(name, animation_value_type=None,
+ vector_animation_type=None, allow_empty=False,
+ none_value=None,
+ simple_vector_bindings=False,
+ separator='Comma',
+ **kwargs)">
+ <%call expr="longhand(name, animation_value_type=animation_value_type, vector=True,
+ simple_vector_bindings=simple_vector_bindings, **kwargs)">
+ #[allow(unused_imports)]
+ use smallvec::SmallVec;
+
+ pub mod single_value {
+ #[allow(unused_imports)]
+ use cssparser::{Parser, BasicParseError};
+ #[allow(unused_imports)]
+ use crate::parser::{Parse, ParserContext};
+ #[allow(unused_imports)]
+ use crate::properties::ShorthandId;
+ #[allow(unused_imports)]
+ use selectors::parser::SelectorParseErrorKind;
+ #[allow(unused_imports)]
+ use style_traits::{ParseError, StyleParseErrorKind};
+ #[allow(unused_imports)]
+ use crate::values::computed::{Context, ToComputedValue};
+ #[allow(unused_imports)]
+ use crate::values::{computed, specified};
+ ${caller.body()}
+ }
+
+ /// The definition of the computed value for ${name}.
+ pub mod computed_value {
+ #[allow(unused_imports)]
+ use crate::values::animated::ToAnimatedValue;
+ #[allow(unused_imports)]
+ use crate::values::resolved::ToResolvedValue;
+ pub use super::single_value::computed_value as single_value;
+ pub use self::single_value::T as SingleComputedValue;
+ % if not allow_empty:
+ use smallvec::SmallVec;
+ % endif
+ use crate::values::computed::ComputedVecIter;
+
+ <%
+ is_shared_list = allow_empty and \
+ data.longhands_by_name[name].style_struct.inherited
+ %>
+
+ // FIXME(emilio): Add an OwnedNonEmptySlice type, and figure out
+ // something for transition-name, which is the only remaining user
+ // of NotInitial.
+ pub type UnderlyingList<T> =
+ % if allow_empty:
+ % if data.longhands_by_name[name].style_struct.inherited:
+ crate::ArcSlice<T>;
+ % else:
+ crate::OwnedSlice<T>;
+ % endif
+ % else:
+ SmallVec<[T; 1]>;
+ % endif
+
+ pub type UnderlyingOwnedList<T> =
+ % if allow_empty:
+ crate::OwnedSlice<T>;
+ % else:
+ SmallVec<[T; 1]>;
+ % endif
+
+
+ /// The generic type defining the animated and resolved values for
+ /// this property.
+ ///
+ /// Making this type generic allows the compiler to figure out the
+ /// animated value for us, instead of having to implement it
+ /// manually for every type we care about.
+ #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToResolvedValue, ToCss)]
+ % if separator == "Comma":
+ #[css(comma)]
+ % endif
+ pub struct OwnedList<T>(
+ % if not allow_empty:
+ #[css(iterable)]
+ % else:
+ #[css(if_empty = "none", iterable)]
+ % endif
+ pub UnderlyingOwnedList<T>,
+ );
+
+ /// The computed value for this property.
+ % if not is_shared_list:
+ pub type ComputedList = OwnedList<single_value::T>;
+ pub use self::OwnedList as List;
+ % else:
+ pub use self::ComputedList as List;
+
+ #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+ % if separator == "Comma":
+ #[css(comma)]
+ % endif
+ pub struct ComputedList(
+ % if not allow_empty:
+ #[css(iterable)]
+ % else:
+ #[css(if_empty = "none", iterable)]
+ % endif
+ % if is_shared_list:
+ #[ignore_malloc_size_of = "Arc"]
+ % endif
+ pub UnderlyingList<single_value::T>,
+ );
+
+ type ResolvedList = OwnedList<<single_value::T as ToResolvedValue>::ResolvedValue>;
+ impl ToResolvedValue for ComputedList {
+ type ResolvedValue = ResolvedList;
+
+ fn to_resolved_value(self, context: &crate::values::resolved::Context) -> Self::ResolvedValue {
+ OwnedList(
+ self.0
+ .iter()
+ .cloned()
+ .map(|v| v.to_resolved_value(context))
+ .collect()
+ )
+ }
+
+ fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
+ % if not is_shared_list:
+ use std::iter::FromIterator;
+ % endif
+ let iter =
+ resolved.0.into_iter().map(ToResolvedValue::from_resolved_value);
+ ComputedList(UnderlyingList::from_iter(iter))
+ }
+ }
+ % endif
+
+ % if simple_vector_bindings:
+ impl From<ComputedList> for UnderlyingList<single_value::T> {
+ #[inline]
+ fn from(l: ComputedList) -> Self {
+ l.0
+ }
+ }
+ impl From<UnderlyingList<single_value::T>> for ComputedList {
+ #[inline]
+ fn from(l: UnderlyingList<single_value::T>) -> Self {
+ List(l)
+ }
+ }
+ % endif
+
+ % if vector_animation_type:
+ % if not animation_value_type:
+ Sorry, this is stupid but needed for now.
+ % endif
+
+ use crate::values::animated::{Animate, ToAnimatedZero, Procedure, lists};
+ use crate::values::distance::{SquaredDistance, ComputeSquaredDistance};
+
+ // FIXME(emilio): For some reason rust thinks that this alias is
+ // unused, even though it's clearly used below?
+ #[allow(unused)]
+ type AnimatedList = OwnedList<<single_value::T as ToAnimatedValue>::AnimatedValue>;
+
+ % if is_shared_list:
+ impl ToAnimatedValue for ComputedList {
+ type AnimatedValue = AnimatedList;
+
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ OwnedList(
+ self.0.iter().map(|v| v.clone().to_animated_value()).collect()
+ )
+ }
+
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ let iter =
+ animated.0.into_iter().map(ToAnimatedValue::from_animated_value);
+ ComputedList(UnderlyingList::from_iter(iter))
+ }
+ }
+ % endif
+
+ impl ToAnimatedZero for AnimatedList {
+ fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
+ }
+
+ impl Animate for AnimatedList {
+ fn animate(
+ &self,
+ other: &Self,
+ procedure: Procedure,
+ ) -> Result<Self, ()> {
+ Ok(OwnedList(
+ lists::${vector_animation_type}::animate(&self.0, &other.0, procedure)?
+ ))
+ }
+ }
+ impl ComputeSquaredDistance for AnimatedList {
+ fn compute_squared_distance(
+ &self,
+ other: &Self,
+ ) -> Result<SquaredDistance, ()> {
+ lists::${vector_animation_type}::squared_distance(&self.0, &other.0)
+ }
+ }
+ % endif
+
+ /// The computed value, effectively a list of single values.
+ pub use self::ComputedList as T;
+
+ pub type Iter<'a, 'cx, 'cx_a> = ComputedVecIter<'a, 'cx, 'cx_a, super::single_value::SpecifiedValue>;
+ }
+
+ /// The specified value of ${name}.
+ #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
+ % if none_value:
+ #[value_info(other_values = "none")]
+ % endif
+ % if separator == "Comma":
+ #[css(comma)]
+ % endif
+ pub struct SpecifiedValue(
+ % if not allow_empty:
+ #[css(iterable)]
+ % else:
+ #[css(if_empty = "none", iterable)]
+ % endif
+ pub crate::OwnedSlice<single_value::SpecifiedValue>,
+ );
+
+ pub fn get_initial_value() -> computed_value::T {
+ % if allow_empty:
+ computed_value::List(Default::default())
+ % else:
+ let mut v = SmallVec::new();
+ v.push(single_value::get_initial_value());
+ computed_value::List(v)
+ % endif
+ }
+
+ pub fn parse<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<SpecifiedValue, ParseError<'i>> {
+ use style_traits::Separator;
+
+ % if allow_empty or none_value:
+ if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
+ % if allow_empty:
+ return Ok(SpecifiedValue(Default::default()))
+ % else:
+ return Ok(SpecifiedValue(crate::OwnedSlice::from(vec![${none_value}])))
+ % endif
+ }
+ % endif
+
+ let v = style_traits::${separator}::parse(input, |parser| {
+ single_value::parse(context, parser)
+ })?;
+ Ok(SpecifiedValue(v.into()))
+ }
+
+ pub use self::single_value::SpecifiedValue as SingleSpecifiedValue;
+
+ % if not simple_vector_bindings and engine == "gecko":
+ impl SpecifiedValue {
+ fn compute_iter<'a, 'cx, 'cx_a>(
+ &'a self,
+ context: &'cx Context<'cx_a>,
+ ) -> computed_value::Iter<'a, 'cx, 'cx_a> {
+ computed_value::Iter::new(context, &self.0)
+ }
+ }
+ % endif
+
+ impl ToComputedValue for SpecifiedValue {
+ type ComputedValue = computed_value::T;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> computed_value::T {
+ % if not is_shared_list:
+ use std::iter::FromIterator;
+ % endif
+ computed_value::List(computed_value::UnderlyingList::from_iter(
+ self.0.iter().map(|i| i.to_computed_value(context))
+ ))
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &computed_value::T) -> Self {
+ let iter = computed.0.iter().map(ToComputedValue::from_computed_value);
+ SpecifiedValue(iter.collect())
+ }
+ }
+ </%call>
+</%def>
+<%def name="longhand(*args, **kwargs)">
+ <%
+ property = data.declare_longhand(*args, **kwargs)
+ if property is None:
+ return ""
+ %>
+ /// ${property.spec}
+ pub mod ${property.ident} {
+ #[allow(unused_imports)]
+ use cssparser::{Parser, BasicParseError, Token};
+ #[allow(unused_imports)]
+ use crate::parser::{Parse, ParserContext};
+ #[allow(unused_imports)]
+ use crate::properties::{UnparsedValue, ShorthandId};
+ #[allow(unused_imports)]
+ use crate::error_reporting::ParseErrorReporter;
+ #[allow(unused_imports)]
+ use crate::properties::longhands;
+ #[allow(unused_imports)]
+ use crate::properties::{LonghandId, LonghandIdSet};
+ #[allow(unused_imports)]
+ use crate::properties::{CSSWideKeyword, ComputedValues, PropertyDeclaration};
+ #[allow(unused_imports)]
+ use crate::properties::style_structs;
+ #[allow(unused_imports)]
+ use selectors::parser::SelectorParseErrorKind;
+ #[allow(unused_imports)]
+ use servo_arc::Arc;
+ #[allow(unused_imports)]
+ use style_traits::{ParseError, StyleParseErrorKind};
+ #[allow(unused_imports)]
+ use crate::values::computed::{Context, ToComputedValue};
+ #[allow(unused_imports)]
+ use crate::values::{computed, generics, specified};
+ #[allow(unused_imports)]
+ use crate::Atom;
+ ${caller.body()}
+ #[allow(unused_variables)]
+ pub unsafe fn cascade_property(
+ declaration: &PropertyDeclaration,
+ context: &mut computed::Context,
+ ) {
+ % if property.logical:
+ declaration.debug_crash("Should physicalize before entering here");
+ % else:
+ context.for_non_inherited_property = ${"false" if property.style_struct.inherited else "true"};
+ % if property.logical_group:
+ debug_assert_eq!(
+ declaration.id().as_longhand().unwrap().logical_group(),
+ LonghandId::${property.camel_case}.logical_group(),
+ );
+ % else:
+ debug_assert_eq!(
+ declaration.id().as_longhand().unwrap(),
+ LonghandId::${property.camel_case},
+ );
+ % endif
+ let specified_value = match *declaration {
+ PropertyDeclaration::CSSWideKeyword(ref wk) => {
+ match wk.keyword {
+ % if not property.style_struct.inherited:
+ CSSWideKeyword::Unset |
+ % endif
+ CSSWideKeyword::Initial => {
+ % if not property.style_struct.inherited:
+ declaration.debug_crash("Unexpected initial or unset for non-inherited property");
+ % else:
+ context.builder.reset_${property.ident}();
+ % endif
+ },
+ % if property.style_struct.inherited:
+ CSSWideKeyword::Unset |
+ % endif
+ CSSWideKeyword::Inherit => {
+ % if property.style_struct.inherited:
+ declaration.debug_crash("Unexpected inherit or unset for inherited property");
+ % else:
+ context.rule_cache_conditions.borrow_mut().set_uncacheable();
+ context.builder.inherit_${property.ident}();
+ % endif
+ }
+ CSSWideKeyword::RevertLayer |
+ CSSWideKeyword::Revert => {
+ declaration.debug_crash("Found revert/revert-layer not deal with");
+ },
+ }
+ return;
+ },
+ #[cfg(debug_assertions)]
+ PropertyDeclaration::WithVariables(..) => {
+ declaration.debug_crash("Found variables not substituted");
+ return;
+ },
+ _ => unsafe {
+ declaration.unchecked_value_as::<${property.specified_type()}>()
+ },
+ };
+
+ % if property.ident in SYSTEM_FONT_LONGHANDS and engine == "gecko":
+ if let Some(sf) = specified_value.get_system() {
+ longhands::system_font::resolve_system_font(sf, context);
+ }
+ % endif
+
+ % if property.is_vector and not property.simple_vector_bindings and engine == "gecko":
+ // In the case of a vector property we want to pass down an
+ // iterator so that this can be computed without allocation.
+ //
+ // However, computing requires a context, but the style struct
+ // being mutated is on the context. We temporarily remove it,
+ // mutate it, and then put it back. Vector longhands cannot
+ // touch their own style struct whilst computing, else this will
+ // panic.
+ let mut s =
+ context.builder.take_${data.current_style_struct.name_lower}();
+ {
+ let iter = specified_value.compute_iter(context);
+ s.set_${property.ident}(iter);
+ }
+ context.builder.put_${data.current_style_struct.name_lower}(s);
+ % else:
+ % if property.boxed:
+ let computed = (**specified_value).to_computed_value(context);
+ % else:
+ let computed = specified_value.to_computed_value(context);
+ % endif
+ context.builder.set_${property.ident}(computed)
+ % endif
+ % endif
+ }
+
+ pub fn parse_declared<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<PropertyDeclaration, ParseError<'i>> {
+ % if property.allow_quirks != "No":
+ parse_quirky(context, input, specified::AllowQuirks::${property.allow_quirks})
+ % else:
+ parse(context, input)
+ % endif
+ % if property.boxed:
+ .map(Box::new)
+ % endif
+ .map(PropertyDeclaration::${property.camel_case})
+ }
+ }
+</%def>
+
+<%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue', cast_to=None)">
+ <%
+ if not values:
+ values = keyword.values_for(engine)
+ maybe_cast = "as %s" % cast_to if cast_to else ""
+ const_type = cast_to if cast_to else "u32"
+ %>
+ #[cfg(feature = "gecko")]
+ impl ${type} {
+ /// Obtain a specified value from a Gecko keyword value
+ ///
+ /// Intended for use with presentation attributes, not style structs
+ pub fn from_gecko_keyword(kw: u32) -> Self {
+ use crate::gecko_bindings::structs;
+ % for value in values:
+ // We can't match on enum values if we're matching on a u32
+ const ${to_rust_ident(value).upper()}: ${const_type}
+ = structs::${keyword.gecko_constant(value)} as ${const_type};
+ % endfor
+ match kw ${maybe_cast} {
+ % for value in values:
+ ${to_rust_ident(value).upper()} => ${type}::${to_camel_case(value)},
+ % endfor
+ _ => panic!("Found unexpected value in style struct for ${keyword.name} property"),
+ }
+ }
+ }
+</%def>
+
+<%def name="gecko_bitflags_conversion(bit_map, gecko_bit_prefix, type, kw_type='u8')">
+ #[cfg(feature = "gecko")]
+ impl ${type} {
+ /// Obtain a specified value from a Gecko keyword value
+ ///
+ /// Intended for use with presentation attributes, not style structs
+ pub fn from_gecko_keyword(kw: ${kw_type}) -> Self {
+ % for gecko_bit in bit_map.values():
+ use crate::gecko_bindings::structs::${gecko_bit_prefix}${gecko_bit};
+ % endfor
+
+ let mut bits = ${type}::empty();
+ % for servo_bit, gecko_bit in bit_map.items():
+ if kw & (${gecko_bit_prefix}${gecko_bit} as ${kw_type}) != 0 {
+ bits |= ${servo_bit};
+ }
+ % endfor
+ bits
+ }
+
+ pub fn to_gecko_keyword(self) -> ${kw_type} {
+ % for gecko_bit in bit_map.values():
+ use crate::gecko_bindings::structs::${gecko_bit_prefix}${gecko_bit};
+ % endfor
+
+ let mut bits: ${kw_type} = 0;
+ // FIXME: if we ensure that the Servo bitflags storage is the same
+ // as Gecko's one, we can just copy it.
+ % for servo_bit, gecko_bit in bit_map.items():
+ if self.contains(${servo_bit}) {
+ bits |= ${gecko_bit_prefix}${gecko_bit} as ${kw_type};
+ }
+ % endfor
+ bits
+ }
+ }
+</%def>
+
+<%def name="single_keyword(name, values, vector=False,
+ needs_conversion=False, **kwargs)">
+ <%
+ keyword_kwargs = {a: kwargs.pop(a, None) for a in [
+ 'gecko_constant_prefix',
+ 'gecko_enum_prefix',
+ 'extra_gecko_values',
+ 'extra_servo_2013_values',
+ 'extra_servo_2020_values',
+ 'gecko_aliases',
+ 'servo_2013_aliases',
+ 'servo_2020_aliases',
+ 'custom_consts',
+ 'gecko_inexhaustive',
+ 'gecko_strip_moz_prefix',
+ ]}
+ %>
+
+ <%def name="inner_body(keyword, needs_conversion=False)">
+ pub use self::computed_value::T as SpecifiedValue;
+ pub mod computed_value {
+ #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+ #[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
+ pub enum T {
+ % for variant in keyword.values_for(engine):
+ <%
+ aliases = []
+ for alias, v in keyword.aliases_for(engine).items():
+ if variant == v:
+ aliases.append(alias)
+ %>
+ % if aliases:
+ #[parse(aliases = "${','.join(sorted(aliases))}")]
+ % endif
+ ${to_camel_case(variant)},
+ % endfor
+ }
+ }
+ #[inline]
+ pub fn get_initial_value() -> computed_value::T {
+ computed_value::T::${to_camel_case(values.split()[0])}
+ }
+ #[inline]
+ pub fn get_initial_specified_value() -> SpecifiedValue {
+ SpecifiedValue::${to_camel_case(values.split()[0])}
+ }
+ #[inline]
+ pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
+ -> Result<SpecifiedValue, ParseError<'i>> {
+ SpecifiedValue::parse(input)
+ }
+
+ % if needs_conversion:
+ <%
+ conversion_values = keyword.values_for(engine) + list(keyword.aliases_for(engine).keys())
+ %>
+ ${gecko_keyword_conversion(keyword, values=conversion_values)}
+ % endif
+ </%def>
+ % if vector:
+ <%call expr="vector_longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
+ ${inner_body(Keyword(name, values, **keyword_kwargs))}
+ % if caller:
+ ${caller.body()}
+ % endif
+ </%call>
+ % else:
+ <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
+ ${inner_body(Keyword(name, values, **keyword_kwargs),
+ needs_conversion=needs_conversion)}
+ % if caller:
+ ${caller.body()}
+ % endif
+ </%call>
+ % endif
+</%def>
+
+<%def name="shorthand(name, sub_properties, derive_serialize=False,
+ derive_value_info=True, **kwargs)">
+<%
+ shorthand = data.declare_shorthand(name, sub_properties.split(), **kwargs)
+ # mako doesn't accept non-string value in parameters with <% %> form, so
+ # we have to workaround it this way.
+ if not isinstance(derive_value_info, bool):
+ derive_value_info = eval(derive_value_info)
+%>
+ % if shorthand:
+ /// ${shorthand.spec}
+ pub mod ${shorthand.ident} {
+ use cssparser::Parser;
+ use crate::parser::ParserContext;
+ use crate::properties::{PropertyDeclaration, SourcePropertyDeclaration, MaybeBoxed, longhands};
+ #[allow(unused_imports)]
+ use selectors::parser::SelectorParseErrorKind;
+ #[allow(unused_imports)]
+ use std::fmt::{self, Write};
+ #[allow(unused_imports)]
+ use style_traits::{ParseError, StyleParseErrorKind};
+ #[allow(unused_imports)]
+ use style_traits::{CssWriter, KeywordsCollectFn, SpecifiedValueInfo, ToCss};
+
+ % if derive_value_info:
+ #[derive(SpecifiedValueInfo)]
+ % endif
+ pub struct Longhands {
+ % for sub_property in shorthand.sub_properties:
+ pub ${sub_property.ident}:
+ % if sub_property.boxed:
+ Box<
+ % endif
+ longhands::${sub_property.ident}::SpecifiedValue
+ % if sub_property.boxed:
+ >
+ % endif
+ ,
+ % endfor
+ }
+
+ /// Represents a serializable set of all of the longhand properties that
+ /// correspond to a shorthand.
+ % if derive_serialize:
+ #[derive(ToCss)]
+ % endif
+ pub struct LonghandsToSerialize<'a> {
+ % for sub_property in shorthand.sub_properties:
+ pub ${sub_property.ident}:
+ % if sub_property.may_be_disabled_in(shorthand, engine):
+ Option<
+ % endif
+ &'a longhands::${sub_property.ident}::SpecifiedValue,
+ % if sub_property.may_be_disabled_in(shorthand, engine):
+ >,
+ % endif
+ % endfor
+ }
+
+ impl<'a> LonghandsToSerialize<'a> {
+ /// Tries to get a serializable set of longhands given a set of
+ /// property declarations.
+ pub fn from_iter(iter: impl Iterator<Item = &'a PropertyDeclaration>) -> Result<Self, ()> {
+ // Define all of the expected variables that correspond to the shorthand
+ % for sub_property in shorthand.sub_properties:
+ let mut ${sub_property.ident} =
+ None::< &'a longhands::${sub_property.ident}::SpecifiedValue>;
+ % endfor
+
+ // Attempt to assign the incoming declarations to the expected variables
+ for declaration in iter {
+ match *declaration {
+ % for sub_property in shorthand.sub_properties:
+ PropertyDeclaration::${sub_property.camel_case}(ref value) => {
+ ${sub_property.ident} = Some(value)
+ },
+ % endfor
+ _ => {}
+ };
+ }
+
+ // If any of the expected variables are missing, return an error
+ match (
+ % for sub_property in shorthand.sub_properties:
+ ${sub_property.ident},
+ % endfor
+ ) {
+
+ (
+ % for sub_property in shorthand.sub_properties:
+ % if sub_property.may_be_disabled_in(shorthand, engine):
+ ${sub_property.ident},
+ % else:
+ Some(${sub_property.ident}),
+ % endif
+ % endfor
+ ) =>
+ Ok(LonghandsToSerialize {
+ % for sub_property in shorthand.sub_properties:
+ ${sub_property.ident},
+ % endfor
+ }),
+ _ => Err(())
+ }
+ }
+ }
+
+ /// Parse the given shorthand and fill the result into the
+ /// `declarations` vector.
+ pub fn parse_into<'i, 't>(
+ declarations: &mut SourcePropertyDeclaration,
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<(), ParseError<'i>> {
+ #[allow(unused_imports)]
+ use crate::properties::{NonCustomPropertyId, LonghandId};
+ input.parse_entirely(|input| parse_value(context, input)).map(|longhands| {
+ % for sub_property in shorthand.sub_properties:
+ % if sub_property.may_be_disabled_in(shorthand, engine):
+ if NonCustomPropertyId::from(LonghandId::${sub_property.camel_case})
+ .allowed_in_ignoring_rule_type(context) {
+ % endif
+ declarations.push(PropertyDeclaration::${sub_property.camel_case}(
+ longhands.${sub_property.ident}
+ ));
+ % if sub_property.may_be_disabled_in(shorthand, engine):
+ }
+ % endif
+ % endfor
+ })
+ }
+
+ /// Try to serialize a given shorthand to a string.
+ pub fn to_css(declarations: &[&PropertyDeclaration], dest: &mut crate::str::CssStringWriter) -> fmt::Result {
+ match LonghandsToSerialize::from_iter(declarations.iter().cloned()) {
+ Ok(longhands) => longhands.to_css(&mut CssWriter::new(dest)),
+ Err(_) => Ok(())
+ }
+ }
+
+ ${caller.body()}
+ }
+ % endif
+</%def>
+
+// A shorthand of kind `<property-1> <property-2>?` where both properties have
+// the same type.
+<%def name="two_properties_shorthand(
+ name,
+ first_property,
+ second_property,
+ parser_function='crate::parser::Parse::parse',
+ **kwargs
+)">
+<%call expr="self.shorthand(name, sub_properties=' '.join([first_property, second_property]), **kwargs)">
+ #[allow(unused_imports)]
+ use crate::parser::Parse;
+ #[allow(unused_imports)]
+ use crate::values::specified;
+
+ fn parse_value<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Longhands, ParseError<'i>> {
+ let parse_one = |c: &ParserContext, input: &mut Parser<'i, 't>| -> Result<
+ crate::properties::longhands::${to_rust_ident(first_property)}::SpecifiedValue,
+ ParseError<'i>
+ > {
+ ${parser_function}(c, input)
+ };
+
+ let first = parse_one(context, input)?;
+ let second =
+ input.try_parse(|input| parse_one(context, input)).unwrap_or_else(|_| first.clone());
+ Ok(expanded! {
+ ${to_rust_ident(first_property)}: first,
+ ${to_rust_ident(second_property)}: second,
+ })
+ }
+
+ impl<'a> ToCss for LonghandsToSerialize<'a> {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
+ let first = &self.${to_rust_ident(first_property)};
+ let second = &self.${to_rust_ident(second_property)};
+
+ first.to_css(dest)?;
+ if first != second {
+ dest.write_char(' ')?;
+ second.to_css(dest)?;
+ }
+ Ok(())
+ }
+ }
+</%call>
+</%def>
+
+<%def name="four_sides_shorthand(name, sub_property_pattern,
+ parser_function='crate::parser::Parse::parse',
+ allow_quirks='No', **kwargs)">
+ <% sub_properties=' '.join(sub_property_pattern % side for side in PHYSICAL_SIDES) %>
+ <%call expr="self.shorthand(name, sub_properties=sub_properties, **kwargs)">
+ #[allow(unused_imports)]
+ use crate::parser::Parse;
+ use crate::values::generics::rect::Rect;
+ #[allow(unused_imports)]
+ use crate::values::specified;
+
+ fn parse_value<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Longhands, ParseError<'i>> {
+ let rect = Rect::parse_with(context, input, |c, i| -> Result<
+ crate::properties::longhands::${to_rust_ident(sub_property_pattern % "top")}::SpecifiedValue,
+ ParseError<'i>
+ > {
+ % if allow_quirks != "No":
+ ${parser_function}_quirky(c, i, specified::AllowQuirks::${allow_quirks})
+ % else:
+ ${parser_function}(c, i)
+ % endif
+ })?;
+ Ok(expanded! {
+ % for index, side in enumerate(["top", "right", "bottom", "left"]):
+ ${to_rust_ident(sub_property_pattern % side)}: rect.${index},
+ % endfor
+ })
+ }
+
+ impl<'a> ToCss for LonghandsToSerialize<'a> {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ let rect = Rect::new(
+ % for side in ["top", "right", "bottom", "left"]:
+ &self.${to_rust_ident(sub_property_pattern % side)},
+ % endfor
+ );
+ rect.to_css(dest)
+ }
+ }
+ </%call>
+</%def>