servo/components/style/properties/helpers.mako.rs
Tiaan Louw 8c1c4073e2 style: Convert RGBA to AbsoluteColor for computed/animated/resolved CSS colors
Computed color values will not be in the correct format, closer to the
one specified by the author.  This also means that colors accross the
code are stored now as AbsoluteColor or StyleAbsoluteColor.  This allows
color space/gamut information to be available for use.

Some animation related test failures had to be changed, because colors
now has greater precision.  Animated a color now causes a lot more
animation updates, which was not initially expected.  See the bug for
discussion.

Differential Revision: https://phabricator.services.mozilla.com/D171021
2023-11-21 15:36:35 +01:00

1034 lines
40 KiB
Rust

/* 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 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::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 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_values',
'gecko_aliases',
'servo_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, Hash, 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>
<%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>