/* 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, computed_type=None, initial_specified_value=None, allow_quirks='No', allow_empty=False, **kwargs)"> <%def name="predefined_type_inner(name, type, initial_value, parse_method)"> #[allow(unused_imports)] use app_units::Au; #[allow(unused_imports)] use cssparser::{Color as CSSParserColor, RGBA}; #[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 { % if computed_type: pub use ${computed_type} as T; % else: pub use crate::values::computed::${type} as T; % endif } % 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=allow_empty or not initial_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> // FIXME (Manishearth): Add computed_value_as_specified argument // and handle the empty case correctly <%doc> To be used in cases where we have a grammar like "<thing> [ , <thing> ]*". Setting allow_empty to False allows for cases where the vector is empty. The grammar for these is usually "none | <thing> [ , <thing> ]*". We assume that the default/initial value is an empty vector for these. `initial_value` need not be defined for these. </%doc> // 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, 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 or allow_empty == "NotInitial": use smallvec::SmallVec; % endif use crate::values::computed::ComputedVecIter; <% is_shared_list = allow_empty and allow_empty != "NotInitial" 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 and allow_empty != "NotInitial": % 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 and allow_empty != "NotInitial": 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::properties::animated_properties::ListAnimation; use crate::values::animated::{Animate, ToAnimatedZero, Procedure}; 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( self.0.animate_${vector_animation_type}(&other.0, procedure)? )) } } impl ComputeSquaredDistance for AnimatedList { fn compute_squared_distance( &self, other: &Self, ) -> Result<SquaredDistance, ()> { self.0.squared_distance_${vector_animation_type}(&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 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 and allow_empty != "NotInitial": 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: if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() { return Ok(SpecifiedValue(Default::default())) } % 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 fn cascade_property( declaration: &PropertyDeclaration, context: &mut computed::Context, ) { context.for_non_inherited_property = % if property.style_struct.inherited: None; % else: Some(LonghandId::${property.camel_case}); % endif let specified_value = match *declaration { PropertyDeclaration::${property.camel_case}(ref value) => value, PropertyDeclaration::CSSWideKeyword(ref declaration) => { debug_assert_eq!(declaration.id, LonghandId::${property.camel_case}); match declaration.keyword { % if not property.style_struct.inherited: CSSWideKeyword::Unset | % endif CSSWideKeyword::Initial => { % if not property.style_struct.inherited: debug_assert!(false, "Should be handled in apply_properties"); % else: context.builder.reset_${property.ident}(); % endif }, % if property.style_struct.inherited: CSSWideKeyword::Unset | % endif CSSWideKeyword::Inherit => { % if property.style_struct.inherited: debug_assert!(false, "Should be handled in apply_properties"); % else: context.rule_cache_conditions.borrow_mut().set_uncacheable(); context.builder.inherit_${property.ident}(); % endif } CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert => unreachable!("Should never get here"), } return; } PropertyDeclaration::WithVariables(..) => { panic!("variables should already have been substituted") } _ => panic!("entered the wrong cascade_property() implementation"), }; % 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 not property.style_struct.inherited and property.logical: context.rule_cache_conditions.borrow_mut() .set_writing_mode_dependency(context.builder.writing_mode); % 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 } 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_str(" ")?; 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> <%def name="logical_setter_helper(name)"> <% side = None size = None corner = None axis = None maybe_side = [s for s in LOGICAL_SIDES if s in name] maybe_size = [s for s in LOGICAL_SIZES if s in name] maybe_corner = [s for s in LOGICAL_CORNERS if s in name] maybe_axis = [s for s in LOGICAL_AXES if name.endswith(s)] if len(maybe_side) == 1: side = maybe_side[0] elif len(maybe_size) == 1: size = maybe_size[0] elif len(maybe_corner) == 1: corner = maybe_corner[0] elif len(maybe_axis) == 1: axis = maybe_axis[0] def phys_ident(side, phy_side): return to_rust_ident(to_phys(name, side, phy_side)) %> % if side is not None: use crate::logical_geometry::PhysicalSide; match wm.${to_rust_ident(side)}_physical_side() { % for phy_side in PHYSICAL_SIDES: PhysicalSide::${phy_side.title()} => { ${caller.inner(physical_ident=phys_ident(side, phy_side))} } % endfor } % elif corner is not None: use crate::logical_geometry::PhysicalCorner; match wm.${to_rust_ident(corner)}_physical_corner() { % for phy_corner in PHYSICAL_CORNERS: PhysicalCorner::${to_camel_case(phy_corner)} => { ${caller.inner(physical_ident=phys_ident(corner, phy_corner))} } % endfor } % elif size is not None: <% # (horizontal, vertical) physical_size = ("height", "width") if size == "inline-size": physical_size = ("width", "height") %> if wm.is_vertical() { ${caller.inner(physical_ident=phys_ident(size, physical_size[1]))} } else { ${caller.inner(physical_ident=phys_ident(size, physical_size[0]))} } % elif axis is not None: <% if axis == "inline": me, other = "x", "y" else: assert(axis == "block") me, other = "y", "x" %> if wm.is_vertical() { ${caller.inner(physical_ident=phys_ident(axis, other))} } else { ${caller.inner(physical_ident=phys_ident(axis, me))} } % else: <% raise Exception("Don't know what to do with logical property %s" % name) %> % endif </%def> <%def name="logical_setter(name)"> /// Set the appropriate physical property for ${name} given a writing mode. pub fn set_${to_rust_ident(name)}(&mut self, v: longhands::${to_rust_ident(name)}::computed_value::T, wm: WritingMode) { <%self:logical_setter_helper name="${name}"> <%def name="inner(physical_ident)"> self.set_${physical_ident}(v) </%def> </%self:logical_setter_helper> } /// Copy the appropriate physical property from another struct for ${name} /// given a writing mode. pub fn copy_${to_rust_ident(name)}_from(&mut self, other: &Self, wm: WritingMode) { <%self:logical_setter_helper name="${name}"> <%def name="inner(physical_ident)"> self.copy_${physical_ident}_from(other) </%def> </%self:logical_setter_helper> } /// Copy the appropriate physical property from another struct for ${name} /// given a writing mode. pub fn reset_${to_rust_ident(name)}(&mut self, other: &Self, wm: WritingMode) { self.copy_${to_rust_ident(name)}_from(other, wm) } /// Get the computed value for the appropriate physical property for /// ${name} given a writing mode. pub fn clone_${to_rust_ident(name)}(&self, wm: WritingMode) -> longhands::${to_rust_ident(name)}::computed_value::T { <%self:logical_setter_helper name="${name}"> <%def name="inner(physical_ident)"> self.clone_${physical_ident}() </%def> </%self:logical_setter_helper> } </%def>