From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../style/properties/shorthands/ui.mako.rs | 444 +++++++++++++++++++++ 1 file changed, 444 insertions(+) create mode 100644 servo/components/style/properties/shorthands/ui.mako.rs (limited to 'servo/components/style/properties/shorthands/ui.mako.rs') diff --git a/servo/components/style/properties/shorthands/ui.mako.rs b/servo/components/style/properties/shorthands/ui.mako.rs new file mode 100644 index 0000000000..1fdb5965fc --- /dev/null +++ b/servo/components/style/properties/shorthands/ui.mako.rs @@ -0,0 +1,444 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +macro_rules! try_parse_one { + ($context: expr, $input: expr, $var: ident, $prop_module: ident) => { + if $var.is_none() { + if let Ok(value) = $input.try_parse(|i| { + $prop_module::single_value::parse($context, i) + }) { + $var = Some(value); + continue; + } + } + }; +} + +<%helpers:shorthand name="transition" + engines="gecko servo-2013 servo-2020" + extra_prefixes="moz:layout.css.prefixes.transitions webkit" + sub_properties="transition-property transition-duration + transition-timing-function + transition-delay" + spec="https://drafts.csswg.org/css-transitions/#propdef-transition"> + use crate::parser::Parse; + % for prop in "delay duration property timing_function".split(): + use crate::properties::longhands::transition_${prop}; + % endfor + use crate::values::specified::TransitionProperty; + + pub fn parse_value<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + struct SingleTransition { + % for prop in "property duration timing_function delay".split(): + transition_${prop}: transition_${prop}::SingleSpecifiedValue, + % endfor + } + + fn parse_one_transition<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + first: bool, + ) -> Result> { + % for prop in "property duration timing_function delay".split(): + let mut ${prop} = None; + % endfor + + let mut parsed = 0; + loop { + parsed += 1; + + try_parse_one!(context, input, duration, transition_duration); + try_parse_one!(context, input, timing_function, transition_timing_function); + try_parse_one!(context, input, delay, transition_delay); + // Must check 'transition-property' after 'transition-timing-function' since + // 'transition-property' accepts any keyword. + if property.is_none() { + if let Ok(value) = input.try_parse(|i| TransitionProperty::parse(context, i)) { + property = Some(value); + continue; + } + + // 'none' is not a valid value for , + // so it's only acceptable as the first item. + if first && input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { + property = Some(TransitionProperty::none()); + continue; + } + } + + parsed -= 1; + break + } + + if parsed != 0 { + Ok(SingleTransition { + % for prop in "property duration timing_function delay".split(): + transition_${prop}: ${prop}.unwrap_or_else(transition_${prop}::single_value + ::get_initial_specified_value), + % endfor + }) + } else { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + } + + % for prop in "property duration timing_function delay".split(): + let mut ${prop}s = Vec::new(); + % endfor + + let mut first = true; + let mut has_transition_property_none = false; + let results = input.parse_comma_separated(|i| { + if has_transition_property_none { + // If you specify transition-property: none, multiple items are invalid. + return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + let transition = parse_one_transition(context, i, first)?; + first = false; + has_transition_property_none = transition.transition_property.is_none(); + Ok(transition) + })?; + for result in results { + % for prop in "property duration timing_function delay".split(): + ${prop}s.push(result.transition_${prop}); + % endfor + } + + Ok(expanded! { + % for prop in "property duration timing_function delay".split(): + transition_${prop}: transition_${prop}::SpecifiedValue(${prop}s.into()), + % endfor + }) + } + + impl<'a> ToCss for LonghandsToSerialize<'a> { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { + use crate::Zero; + use style_traits::values::SequenceWriter; + + let property_len = self.transition_property.0.len(); + + // There are two cases that we can do shorthand serialization: + // * when all value lists have the same length, or + // * when transition-property is none, and other value lists have exactly one item. + if property_len == 0 { + % for name in "duration delay timing_function".split(): + if self.transition_${name}.0.len() != 1 { + return Ok(()); + } + % endfor + } else { + % for name in "duration delay timing_function".split(): + if self.transition_${name}.0.len() != property_len { + return Ok(()); + } + % endfor + } + + // Representative length. + let len = self.transition_duration.0.len(); + + for i in 0..len { + if i != 0 { + dest.write_str(", ")?; + } + + let has_duration = !self.transition_duration.0[i].is_zero(); + let has_timing = !self.transition_timing_function.0[i].is_ease(); + let has_delay = !self.transition_delay.0[i].is_zero(); + let has_any = has_duration || has_timing || has_delay; + + let mut writer = SequenceWriter::new(dest, " "); + + if property_len == 0 { + writer.raw_item("none")?; + } else if !self.transition_property.0[i].is_all() || !has_any { + writer.item(&self.transition_property.0[i])?; + } + + // In order to avoid ambiguity, we have to serialize duration if we have delay. + if has_duration || has_delay { + writer.item(&self.transition_duration.0[i])?; + } + + if has_timing { + writer.item(&self.transition_timing_function.0[i])?; + } + + if has_delay { + writer.item(&self.transition_delay.0[i])?; + } + } + Ok(()) + } + } + + +<%helpers:shorthand name="animation" + engines="gecko servo-2013 servo-2020" + extra_prefixes="moz:layout.css.prefixes.animations webkit" + sub_properties="animation-name animation-duration + animation-timing-function animation-delay + animation-iteration-count animation-direction + animation-fill-mode animation-play-state animation-timeline" + rule_types_allowed="Style" + spec="https://drafts.csswg.org/css-animations/#propdef-animation"> + <% + props = "name timeline duration timing_function delay iteration_count \ + direction fill_mode play_state".split() + %> + % for prop in props: + use crate::properties::longhands::animation_${prop}; + % endfor + + pub fn parse_value<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + struct SingleAnimation { + % for prop in props: + animation_${prop}: animation_${prop}::SingleSpecifiedValue, + % endfor + } + + fn parse_one_animation<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + % for prop in props: + let mut ${prop} = None; + % endfor + + let mut parsed = 0; + // NB: Name must be the last one here so that keywords valid for other + // longhands are not interpreted as names. + // + // Also, duration must be before delay, see + // https://drafts.csswg.org/css-animations/#typedef-single-animation + loop { + parsed += 1; + try_parse_one!(context, input, duration, animation_duration); + try_parse_one!(context, input, timing_function, animation_timing_function); + try_parse_one!(context, input, delay, animation_delay); + try_parse_one!(context, input, iteration_count, animation_iteration_count); + try_parse_one!(context, input, direction, animation_direction); + try_parse_one!(context, input, fill_mode, animation_fill_mode); + try_parse_one!(context, input, play_state, animation_play_state); + try_parse_one!(context, input, name, animation_name); + if static_prefs::pref!("layout.css.scroll-driven-animations.enabled") { + try_parse_one!(context, input, timeline, animation_timeline); + } + + parsed -= 1; + break + } + + // If nothing is parsed, this is an invalid entry. + if parsed == 0 { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } else { + Ok(SingleAnimation { + % for prop in props: + animation_${prop}: ${prop}.unwrap_or_else(animation_${prop}::single_value + ::get_initial_specified_value), + % endfor + }) + } + } + + % for prop in props: + let mut ${prop}s = vec![]; + % endfor + + let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?; + for result in results.into_iter() { + % for prop in props: + ${prop}s.push(result.animation_${prop}); + % endfor + } + + Ok(expanded! { + % for prop in props: + animation_${prop}: animation_${prop}::SpecifiedValue(${prop}s.into()), + % endfor + }) + } + + impl<'a> ToCss for LonghandsToSerialize<'a> { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { + let len = self.animation_name.0.len(); + // There should be at least one declared value + if len == 0 { + return Ok(()); + } + + // If any value list length is differs then we don't do a shorthand serialization + // either. + % for name in props[2:]: + if len != self.animation_${name}.0.len() { + return Ok(()) + } + % endfor + + // If the preference of animation-timeline is disabled, `self.animation_timeline` is + // None. + if self.animation_timeline.map_or(false, |v| len != v.0.len()) { + return Ok(()); + } + + for i in 0..len { + if i != 0 { + dest.write_str(", ")?; + } + + % for name in props[2:]: + self.animation_${name}.0[i].to_css(dest)?; + dest.write_char(' ')?; + % endfor + + self.animation_name.0[i].to_css(dest)?; + + // Based on the spec, the default values of other properties must be output in at + // least the cases necessary to distinguish an animation-name. The serialization + // order of animation-timeline is always later than animation-name, so it's fine + // to not serialize it if it is the default value. It's still possible to + // distinguish them (because we always serialize animation-name). + // https://drafts.csswg.org/css-animations-1/#animation + // https://drafts.csswg.org/css-animations-2/#typedef-single-animation + // + // Note: it's also fine to always serialize this. However, it seems Blink + // doesn't serialize default animation-timeline now, so we follow the same rule. + if let Some(ref timeline) = self.animation_timeline { + if !timeline.0[i].is_auto() { + dest.write_char(' ')?; + timeline.0[i].to_css(dest)?; + } + } + } + Ok(()) + } + } + + +<%helpers:shorthand + engines="gecko" + name="scroll-timeline" + sub_properties="scroll-timeline-name scroll-timeline-axis" + gecko_pref="layout.css.scroll-driven-animations.enabled", + spec="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-shorthand" +> + pub fn parse_value<'i>( + context: &ParserContext, + input: &mut Parser<'i, '_>, + ) -> Result> { + use crate::properties::longhands::{scroll_timeline_axis, scroll_timeline_name}; + + let mut names = Vec::with_capacity(1); + let mut axes = Vec::with_capacity(1); + input.parse_comma_separated(|input| { + let name = scroll_timeline_name::single_value::parse(context, input)?; + let axis = input.try_parse(|i| scroll_timeline_axis::single_value::parse(context, i)); + + names.push(name); + axes.push(axis.unwrap_or_default()); + + Ok(()) + })?; + + Ok(expanded! { + scroll_timeline_name: scroll_timeline_name::SpecifiedValue(names.into()), + scroll_timeline_axis: scroll_timeline_axis::SpecifiedValue(axes.into()), + }) + } + + impl<'a> ToCss for LonghandsToSerialize<'a> { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { + // If any value list length is differs then we don't do a shorthand serialization + // either. + let len = self.scroll_timeline_name.0.len(); + if len != self.scroll_timeline_axis.0.len() { + return Ok(()); + } + + for i in 0..len { + if i != 0 { + dest.write_str(", ")?; + } + + self.scroll_timeline_name.0[i].to_css(dest)?; + + if self.scroll_timeline_axis.0[i] != Default::default() { + dest.write_char(' ')?; + self.scroll_timeline_axis.0[i].to_css(dest)?; + } + + } + Ok(()) + } + } + + +// Note: view-timeline shorthand doesn't take view-timeline-inset into account. +<%helpers:shorthand + engines="gecko" + name="view-timeline" + sub_properties="view-timeline-name view-timeline-axis" + gecko_pref="layout.css.scroll-driven-animations.enabled", + spec="https://drafts.csswg.org/scroll-animations-1/#view-timeline-shorthand" +> + pub fn parse_value<'i>( + context: &ParserContext, + input: &mut Parser<'i, '_>, + ) -> Result> { + use crate::properties::longhands::{view_timeline_axis, view_timeline_name}; + + let mut names = Vec::with_capacity(1); + let mut axes = Vec::with_capacity(1); + input.parse_comma_separated(|input| { + let name = view_timeline_name::single_value::parse(context, input)?; + let axis = input.try_parse(|i| view_timeline_axis::single_value::parse(context, i)); + + names.push(name); + axes.push(axis.unwrap_or_default()); + + Ok(()) + })?; + + Ok(expanded! { + view_timeline_name: view_timeline_name::SpecifiedValue(names.into()), + view_timeline_axis: view_timeline_axis::SpecifiedValue(axes.into()), + }) + } + + impl<'a> ToCss for LonghandsToSerialize<'a> { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { + // If any value list length is differs then we don't do a shorthand serialization + // either. + let len = self.view_timeline_name.0.len(); + if len != self.view_timeline_axis.0.len() { + return Ok(()); + } + + for i in 0..len { + if i != 0 { + dest.write_str(", ")?; + } + + self.view_timeline_name.0[i].to_css(dest)?; + + if self.view_timeline_axis.0[i] != Default::default() { + dest.write_char(' ')?; + self.view_timeline_axis.0[i].to_css(dest)?; + } + + } + Ok(()) + } + } + -- cgit v1.2.3