use crate::derives::*;
use crate::parser::{Parse, ParserContext};
use crate::properties::{NonCustomPropertyId, PropertyId, ShorthandId};
use crate::values::generics::animation as generics;
use crate::values::generics::position::{IsTreeScoped, TreeScoped};
use crate::values::specified::{LengthPercentage, NonNegativeNumber, Time};
use crate::values::{AtomIdent, CustomIdent, DashedIdent, KeyframesName};
use crate::Atom;
use cssparser::{match_ignore_ascii_case, Parser};
use std::fmt::{self, Write};
use style_traits::{
CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss,
ToTyped,
};
#[derive(
Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
)]
#[repr(u8)]
pub enum TransitionProperty {
NonCustom(NonCustomPropertyId),
Custom(Atom),
Unsupported(CustomIdent),
}
impl ToCss for TransitionProperty {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> 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 ToTyped for TransitionProperty {}
impl Parse for TransitionProperty {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
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(..) => {
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) {
f(&["all"]);
}
}
impl TransitionProperty {
#[inline]
pub fn none() -> Self {
TransitionProperty::Unsupported(CustomIdent(atom!("none")))
}
#[inline]
pub fn is_none(&self) -> bool {
matches!(*self, TransitionProperty::Unsupported(ref ident) if ident.0 == atom!("none"))
}
#[inline]
pub fn all() -> Self {
TransitionProperty::NonCustom(NonCustomPropertyId::from_shorthand(ShorthandId::All))
}
#[inline]
pub fn is_all(&self) -> bool {
self == &TransitionProperty::NonCustom(NonCustomPropertyId::from_shorthand(
ShorthandId::All,
))
}
}
#[derive(
Clone,
Copy,
Debug,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[repr(u8)]
pub enum TransitionBehavior {
Normal,
AllowDiscrete,
}
impl TransitionBehavior {
#[inline]
pub fn normal() -> Self {
Self::Normal
}
#[inline]
pub fn is_normal(&self) -> bool {
matches!(*self, Self::Normal)
}
}
pub type AnimationDuration = generics::GenericAnimationDuration<Time>;
impl Parse for AnimationDuration {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if static_prefs::pref!("layout.css.scroll-driven-animations.enabled")
&& input.try_parse(|i| i.expect_ident_matching("auto")).is_ok()
{
return Ok(Self::auto());
}
Time::parse_non_negative(context, input).map(AnimationDuration::Time)
}
}
#[derive(
Copy, Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
)]
pub enum AnimationIterationCount {
Number(NonNegativeNumber),
Infinite,
}
impl AnimationIterationCount {
#[inline]
pub fn one() -> Self {
Self::Number(NonNegativeNumber::new(1.0))
}
#[inline]
pub fn is_one(&self) -> bool {
*self == Self::one()
}
}
#[derive(
Clone,
Debug,
Eq,
Hash,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[value_info(other_values = "none")]
#[repr(C)]
pub struct AnimationName(pub KeyframesName);
impl AnimationName {
pub fn as_atom(&self) -> Option<&Atom> {
if self.is_none() {
return None;
}
Some(self.0.as_atom())
}
pub fn none() -> Self {
AnimationName(KeyframesName::none())
}
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<Self, ParseError<'i>> {
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()))
}
}
#[derive(
Copy,
Clone,
Debug,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[repr(u8)]
#[allow(missing_docs)]
pub enum AnimationDirection {
Normal,
Reverse,
Alternate,
AlternateReverse,
}
impl AnimationDirection {
#[inline]
pub fn match_keywords(name: &AnimationName) -> bool {
if let Some(name) = name.as_atom() {
#[cfg(feature = "gecko")]
return name.with_str(|n| Self::from_ident(n).is_ok());
#[cfg(feature = "servo")]
return Self::from_ident(name).is_ok();
}
false
}
}
#[derive(
Copy,
Clone,
Debug,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[repr(u8)]
#[allow(missing_docs)]
pub enum AnimationPlayState {
Running,
Paused,
}
impl AnimationPlayState {
#[inline]
pub fn match_keywords(name: &AnimationName) -> bool {
if let Some(name) = name.as_atom() {
#[cfg(feature = "gecko")]
return name.with_str(|n| Self::from_ident(n).is_ok());
#[cfg(feature = "servo")]
return Self::from_ident(name).is_ok();
}
false
}
}
#[derive(
Copy,
Clone,
Debug,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[repr(u8)]
#[allow(missing_docs)]
pub enum AnimationFillMode {
None,
Forwards,
Backwards,
Both,
}
impl AnimationFillMode {
#[inline]
pub fn match_keywords(name: &AnimationName) -> bool {
if let Some(name) = name.as_atom() {
#[cfg(feature = "gecko")]
return name.with_str(|n| Self::from_ident(n).is_ok());
#[cfg(feature = "servo")]
return Self::from_ident(name).is_ok();
}
false
}
}
#[derive(
Copy,
Clone,
Debug,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[repr(u8)]
#[allow(missing_docs)]
pub enum AnimationComposition {
Replace,
Add,
Accumulate,
}
#[derive(
Copy,
Clone,
Debug,
Eq,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum Scroller {
Nearest,
Root,
#[css(keyword = "self")]
SelfElement,
}
impl Scroller {
#[inline]
fn is_default(&self) -> bool {
matches!(*self, Self::Nearest)
}
}
impl Default for Scroller {
fn default() -> Self {
Self::Nearest
}
}
#[derive(
Copy,
Clone,
Debug,
Eq,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[repr(u8)]
pub enum ScrollAxis {
Block = 0,
Inline = 1,
X = 2,
Y = 3,
}
impl ScrollAxis {
#[inline]
pub fn is_default(&self) -> bool {
matches!(*self, Self::Block)
}
}
impl Default for ScrollAxis {
fn default() -> Self {
Self::Block
}
}
#[derive(
Copy,
Clone,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[css(function = "scroll")]
#[repr(C)]
pub struct ScrollFunction {
#[css(skip_if = "Scroller::is_default")]
pub scroller: Scroller,
#[css(skip_if = "ScrollAxis::is_default")]
pub axis: ScrollAxis,
}
impl ScrollFunction {
fn parse_arguments<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
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<LengthPercentage> {
fn parse_arguments<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
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(),
})
}
}
pub type TimelineName = TreeScoped<TimelineIdent>;
impl TimelineName {
pub fn none() -> Self {
Self::with_default_level(TimelineIdent::none())
}
}
#[derive(
Clone,
Debug,
Eq,
Hash,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct TimelineIdent(DashedIdent);
impl TimelineIdent {
pub fn none() -> Self {
Self(DashedIdent::empty())
}
pub fn is_none(&self) -> bool {
self.0.is_empty()
}
}
impl IsTreeScoped for TimelineIdent {
fn is_tree_scoped(&self) -> bool {
!self.is_none()
}
}
impl Parse for TimelineIdent {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
return Ok(Self::none());
}
DashedIdent::parse(context, input).map(TimelineIdent)
}
}
impl ToCss for TimelineIdent {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
if self.is_none() {
return dest.write_str("none");
}
self.0.to_css(dest)
}
}
impl ToTyped for TimelineName {}
pub type AnimationTimeline = generics::GenericAnimationTimeline<LengthPercentage>;
impl Parse for AnimationTimeline {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
use crate::values::generics::animation::ViewFunction;
if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
return Ok(Self::Auto);
}
if let Ok(name) = input.try_parse(|i| TimelineName::parse(context, i)) {
return Ok(AnimationTimeline::Timeline(name));
}
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())
))
},
}
})
}
}
pub type ViewTimelineInset = generics::GenericViewTimelineInset<LengthPercentage>;
impl Parse for ViewTimelineInset {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
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 })
}
}
#[derive(
Clone,
Debug,
Eq,
Hash,
PartialEq,
MallocSizeOf,
SpecifiedValueInfo,
ToCss,
ToComputedValue,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[repr(transparent)]
#[typed(todo_derive_fields)]
#[value_info(other_values = "none, match-element")]
pub struct ViewTransitionNameKeyword(AtomIdent);
impl ViewTransitionNameKeyword {
pub fn none() -> Self {
Self(AtomIdent::new(atom!("none")))
}
}
impl IsTreeScoped for ViewTransitionNameKeyword {
fn is_tree_scoped(&self) -> bool {
self.0 .0 != atom!("none")
}
}
impl Parse for ViewTransitionNameKeyword {
fn parse<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let location = input.current_source_location();
let ident = input.expect_ident()?;
if ident.eq_ignore_ascii_case("none") {
return Ok(Self::none());
}
if ident.eq_ignore_ascii_case("match-element") {
return Ok(Self(AtomIdent::new(atom!("match-element"))));
}
CustomIdent::from_ident(location, ident, &["auto"]).map(|i| Self(AtomIdent::new(i.0)))
}
}
pub type ViewTransitionName = TreeScoped<ViewTransitionNameKeyword>;
impl ViewTransitionName {
pub fn none() -> Self {
Self::with_default_level(ViewTransitionNameKeyword::none())
}
}
#[derive(
Clone,
Debug,
Default,
Eq,
Hash,
PartialEq,
MallocSizeOf,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[repr(C)]
#[value_info(other_values = "none")]
pub struct ViewTransitionClassList(
#[css(iterable, if_empty = "none")]
#[ignore_malloc_size_of = "Arc"]
crate::ArcSlice<CustomIdent>,
);
impl IsTreeScoped for ViewTransitionClassList {
fn is_tree_scoped(&self) -> bool {
!self.is_none()
}
}
impl ViewTransitionClassList {
pub fn none() -> Self {
Self(Default::default())
}
pub fn is_none(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &CustomIdent> {
self.0.iter()
}
}
impl Parse for ViewTransitionClassList {
fn parse<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
use style_traits::{Separator, Space};
if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
return Ok(Self::none());
}
Ok(Self(crate::ArcSlice::from_iter(
Space::parse(input, |i| CustomIdent::parse(i, &["none"]))?.into_iter(),
)))
}
}
pub type ViewTransitionClass = TreeScoped<ViewTransitionClassList>;
impl ViewTransitionClass {
pub fn none() -> Self {
Self::with_default_level(ViewTransitionClassList::none())
}
}
#[derive(
Copy,
Clone,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[repr(u8)]
pub enum TimelineRangeName {
#[css(skip)]
Normal,
#[css(skip)]
None,
Cover,
Contain,
Entry,
Exit,
EntryCrossing,
ExitCrossing,
Scroll,
}
impl TimelineRangeName {
#[inline]
pub fn is_normal(&self) -> bool {
matches!(*self, Self::Normal)
}
#[inline]
pub fn is_none(&self) -> bool {
matches!(*self, Self::None)
}
}
pub type AnimationRangeValue = generics::GenericAnimationRangeValue<LengthPercentage>;
fn parse_animation_range<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
default: LengthPercentage,
) -> Result<AnimationRangeValue, ParseError<'i>> {
if input
.try_parse(|i| i.expect_ident_matching("normal"))
.is_ok()
{
return Ok(AnimationRangeValue::normal(default));
}
if let Ok(lp) = input.try_parse(|i| LengthPercentage::parse(context, i)) {
return Ok(AnimationRangeValue::length_percentage(lp));
}
let name = TimelineRangeName::parse(input)?;
let lp = input
.try_parse(|i| LengthPercentage::parse(context, i))
.unwrap_or(default);
Ok(AnimationRangeValue::new(name, lp))
}
pub type AnimationRangeStart = generics::GenericAnimationRangeStart<LengthPercentage>;
impl Parse for AnimationRangeStart {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
parse_animation_range(context, input, LengthPercentage::zero_percent()).map(Self)
}
}
pub type AnimationRangeEnd = generics::GenericAnimationRangeEnd<LengthPercentage>;
impl Parse for AnimationRangeEnd {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
parse_animation_range(context, input, LengthPercentage::hundred_percent()).map(Self)
}
}