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 --- .../components/style/values/specified/animation.rs | 463 +++++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100644 servo/components/style/values/specified/animation.rs (limited to 'servo/components/style/values/specified/animation.rs') diff --git a/servo/components/style/values/specified/animation.rs b/servo/components/style/values/specified/animation.rs new file mode 100644 index 0000000000..e7bbf26fb3 --- /dev/null +++ b/servo/components/style/values/specified/animation.rs @@ -0,0 +1,463 @@ +/* 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/. */ + +//! Specified types for properties related to animations and transitions. + +use crate::parser::{Parse, ParserContext}; +use crate::properties::{NonCustomPropertyId, PropertyId, ShorthandId}; +use crate::values::generics::animation as generics; +use crate::values::specified::{LengthPercentage, NonNegativeNumber}; +use crate::values::{CustomIdent, KeyframesName, TimelineName}; +use crate::Atom; +use cssparser::Parser; +use std::fmt::{self, Write}; +use style_traits::{ + CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss, +}; + +/// A given transition property, that is either `All`, a longhand or shorthand +/// property, or an unsupported or custom property. +#[derive( + Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, +)] +#[repr(u8)] +pub enum TransitionProperty { + /// A non-custom property. + NonCustom(NonCustomPropertyId), + /// A custom property. + Custom(Atom), + /// Unrecognized property which could be any non-transitionable, custom property, or + /// unknown property. + Unsupported(CustomIdent), +} + +impl ToCss for TransitionProperty { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + match *self { + TransitionProperty::NonCustom(ref id) => id.to_css(dest), + TransitionProperty::Custom(ref name) => { + dest.write_str("--")?; + crate::values::serialize_atom_name(name, dest) + }, + TransitionProperty::Unsupported(ref i) => i.to_css(dest), + } + } +} + +impl Parse for TransitionProperty { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let location = input.current_source_location(); + let ident = input.expect_ident()?; + + let id = match PropertyId::parse_ignoring_rule_type(&ident, context) { + Ok(id) => id, + Err(..) => { + // None is not acceptable as a single transition-property. + return Ok(TransitionProperty::Unsupported(CustomIdent::from_ident( + location, + ident, + &["none"], + )?)); + }, + }; + + Ok(match id { + PropertyId::NonCustom(id) => TransitionProperty::NonCustom(id.unaliased()), + PropertyId::Custom(name) => TransitionProperty::Custom(name), + }) + } +} + +impl SpecifiedValueInfo for TransitionProperty { + fn collect_completion_keywords(f: KeywordsCollectFn) { + // `transition-property` can actually accept all properties and + // arbitrary identifiers, but `all` is a special one we'd like + // to list. + f(&["all"]); + } +} + +impl TransitionProperty { + /// Returns the `none` value. + #[inline] + pub fn none() -> Self { + TransitionProperty::Unsupported(CustomIdent(atom!("none"))) + } + + /// Returns whether we're the `none` value. + #[inline] + pub fn is_none(&self) -> bool { + matches!(*self, TransitionProperty::Unsupported(ref ident) if ident.0 == atom!("none")) + } + + /// Returns `all`. + #[inline] + pub fn all() -> Self { + TransitionProperty::NonCustom(NonCustomPropertyId::from_shorthand(ShorthandId::All)) + } + + /// Returns true if it is `all`. + #[inline] + pub fn is_all(&self) -> bool { + self == &TransitionProperty::NonCustom(NonCustomPropertyId::from_shorthand( + ShorthandId::All, + )) + } +} + +/// https://drafts.csswg.org/css-animations/#animation-iteration-count +#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem)] +pub enum AnimationIterationCount { + /// A `` value. + Number(NonNegativeNumber), + /// The `infinite` keyword. + Infinite, +} + +impl AnimationIterationCount { + /// Returns the value `1.0`. + #[inline] + pub fn one() -> Self { + Self::Number(NonNegativeNumber::new(1.0)) + } +} + +/// A value for the `animation-name` property. +#[derive( + Clone, + Debug, + Eq, + Hash, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[value_info(other_values = "none")] +#[repr(C)] +pub struct AnimationName(pub KeyframesName); + +impl AnimationName { + /// Get the name of the animation as an `Atom`. + pub fn as_atom(&self) -> Option<&Atom> { + if self.is_none() { + return None; + } + Some(self.0.as_atom()) + } + + /// Returns the `none` value. + pub fn none() -> Self { + AnimationName(KeyframesName::none()) + } + + /// Returns whether this is the none value. + pub fn is_none(&self) -> bool { + self.0.is_none() + } +} + +impl Parse for AnimationName { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + if let Ok(name) = input.try_parse(|input| KeyframesName::parse(context, input)) { + return Ok(AnimationName(name)); + } + + input.expect_ident_matching("none")?; + Ok(AnimationName(KeyframesName::none())) + } +} + +/// https://drafts.csswg.org/css-animations/#propdef-animation-direction +#[derive(Copy, Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)] +#[repr(u8)] +#[allow(missing_docs)] +pub enum AnimationDirection { + Normal, + Reverse, + Alternate, + AlternateReverse, +} + +/// https://drafts.csswg.org/css-animations/#animation-play-state +#[derive(Copy, Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)] +#[repr(u8)] +#[allow(missing_docs)] +pub enum AnimationPlayState { + Running, + Paused, +} + +/// https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode +#[derive(Copy, Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)] +#[repr(u8)] +#[allow(missing_docs)] +pub enum AnimationFillMode { + None, + Forwards, + Backwards, + Both, +} + +/// https://drafts.csswg.org/css-animations-2/#animation-composition +#[derive(Copy, Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)] +#[repr(u8)] +#[allow(missing_docs)] +pub enum AnimationComposition { + Replace, + Add, + Accumulate, +} + +/// A value for the used in scroll(). +/// +/// https://drafts.csswg.org/scroll-animations-1/rewrite#typedef-scroller +#[derive( + Copy, + Clone, + Debug, + Eq, + Hash, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(u8)] +pub enum Scroller { + /// The nearest ancestor scroll container. (Default.) + Nearest, + /// The document viewport as the scroll container. + Root, + /// Specifies to use the element’s own principal box as the scroll container. + #[css(keyword = "self")] + SelfElement, +} + +impl Scroller { + /// Returns true if it is default. + #[inline] + fn is_default(&self) -> bool { + matches!(*self, Self::Nearest) + } +} + +impl Default for Scroller { + fn default() -> Self { + Self::Nearest + } +} + +/// A value for the used in scroll(), or a value for {scroll|view}-timeline-axis. +/// +/// https://drafts.csswg.org/scroll-animations-1/#typedef-axis +/// https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-axis +/// https://drafts.csswg.org/scroll-animations-1/#view-timeline-axis +#[derive( + Copy, + Clone, + Debug, + Eq, + Hash, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(u8)] +pub enum ScrollAxis { + /// The block axis of the scroll container. (Default.) + Block = 0, + /// The inline axis of the scroll container. + Inline = 1, + /// The vertical block axis of the scroll container. + Vertical = 2, + /// The horizontal axis of the scroll container. + Horizontal = 3, +} + +impl ScrollAxis { + /// Returns true if it is default. + #[inline] + pub fn is_default(&self) -> bool { + matches!(*self, Self::Block) + } +} + +impl Default for ScrollAxis { + fn default() -> Self { + Self::Block + } +} + +/// The scroll() notation. +/// https://drafts.csswg.org/scroll-animations-1/#scroll-notation +#[derive( + Copy, + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[css(function = "scroll")] +#[repr(C)] +pub struct ScrollFunction { + /// The scroll container element whose scroll position drives the progress of the timeline. + #[css(skip_if = "Scroller::is_default")] + pub scroller: Scroller, + /// The axis of scrolling that drives the progress of the timeline. + #[css(skip_if = "ScrollAxis::is_default")] + pub axis: ScrollAxis, +} + +impl ScrollFunction { + /// Parse the inner function arguments of `scroll()`. + fn parse_arguments<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { + // = scroll( [ || ]? ) + // https://drafts.csswg.org/scroll-animations-1/#funcdef-scroll + let mut scroller = None; + let mut axis = None; + loop { + if scroller.is_none() { + scroller = input.try_parse(Scroller::parse).ok(); + } + + if axis.is_none() { + axis = input.try_parse(ScrollAxis::parse).ok(); + if axis.is_some() { + continue; + } + } + break; + } + + Ok(Self { + scroller: scroller.unwrap_or_default(), + axis: axis.unwrap_or_default(), + }) + } +} + +impl generics::ViewFunction { + /// Parse the inner function arguments of `view()`. + fn parse_arguments<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + // = view( [ || <'view-timeline-inset'> ]? ) + // https://drafts.csswg.org/scroll-animations-1/#funcdef-view + let mut axis = None; + let mut inset = None; + loop { + if axis.is_none() { + axis = input.try_parse(ScrollAxis::parse).ok(); + } + + if inset.is_none() { + inset = input + .try_parse(|i| ViewTimelineInset::parse(context, i)) + .ok(); + if inset.is_some() { + continue; + } + } + break; + } + + Ok(Self { + inset: inset.unwrap_or_default(), + axis: axis.unwrap_or_default(), + }) + } +} + +/// A specified value for the `animation-timeline` property. +pub type AnimationTimeline = generics::GenericAnimationTimeline; + +impl Parse for AnimationTimeline { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + use crate::values::generics::animation::ViewFunction; + + // = auto | none | | | + // https://drafts.csswg.org/css-animations-2/#typedef-single-animation-timeline + + if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() { + return Ok(Self::Auto); + } + + if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { + return Ok(AnimationTimeline::Timeline(TimelineName::none())); + } + + if let Ok(name) = input.try_parse(|i| TimelineName::parse(context, i)) { + return Ok(AnimationTimeline::Timeline(name)); + } + + // Parse possible functions + let location = input.current_source_location(); + let function = input.expect_function()?.clone(); + input.parse_nested_block(move |i| { + match_ignore_ascii_case! { &function, + "scroll" => ScrollFunction::parse_arguments(i).map(Self::Scroll), + "view" => ViewFunction::parse_arguments(context, i).map(Self::View), + _ => { + Err(location.new_custom_error( + StyleParseErrorKind::UnexpectedFunction(function.clone()) + )) + }, + } + }) + } +} + +/// A value for the scroll-timeline-name or view-timeline-name. +pub type ScrollTimelineName = AnimationName; + +/// A specified value for the `view-timeline-inset` property. +pub type ViewTimelineInset = generics::GenericViewTimelineInset; + +impl Parse for ViewTimelineInset { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + use crate::values::specified::LengthPercentageOrAuto; + + let start = LengthPercentageOrAuto::parse(context, input)?; + let end = match input.try_parse(|input| LengthPercentageOrAuto::parse(context, input)) { + Ok(end) => end, + Err(_) => start.clone(), + }; + + Ok(Self { start, end }) + } +} -- cgit v1.2.3