/* 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 box properties. use crate::custom_properties::Name as CustomPropertyName; use crate::parser::{Parse, ParserContext}; use crate::properties::{LonghandId, PropertyDeclarationId, PropertyFlags}; use crate::properties::{PropertyId, ShorthandId}; use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount; use crate::values::generics::box_::Perspective as GenericPerspective; use crate::values::generics::box_::VerticalAlign as GenericVerticalAlign; use crate::values::specified::length::{LengthPercentage, NonNegativeLength}; use crate::values::specified::{AllowQuirks, Number}; use crate::values::{CustomIdent, KeyframesName}; use crate::Atom; use cssparser::Parser; use selectors::parser::SelectorParseErrorKind; use std::fmt::{self, Write}; use style_traits::{CssWriter, KeywordsCollectFn, ParseError}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; #[cfg(feature = "gecko")] fn moz_display_values_enabled(context: &ParserContext) -> bool { use crate::gecko_bindings::structs; context.in_ua_or_chrome_sheet() || unsafe { structs::StaticPrefs_sVarCache_layout_css_xul_display_values_content_enabled } } #[cfg(feature = "gecko")] fn moz_box_display_values_enabled(context: &ParserContext) -> bool { use crate::gecko_bindings::structs; context.in_ua_or_chrome_sheet() || unsafe { structs::StaticPrefs_sVarCache_layout_css_xul_box_display_values_content_enabled } } /// Defines an element’s display type, which consists of /// the two basic qualities of how an element generates boxes /// /// /// /// NOTE(emilio): Order is important in Gecko! /// /// If you change it, make sure to take a look at the /// FrameConstructionDataByDisplay stuff (both the XUL and non-XUL version), and /// ensure it's still correct! #[allow(missing_docs)] #[derive( Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[repr(u8)] pub enum Display { None = 0, Block, #[cfg(feature = "gecko")] FlowRoot, Inline, InlineBlock, ListItem, Table, InlineTable, TableRowGroup, TableColumn, TableColumnGroup, TableHeaderGroup, TableFooterGroup, TableRow, TableCell, TableCaption, #[parse(aliases = "-webkit-flex")] Flex, #[parse(aliases = "-webkit-inline-flex")] InlineFlex, #[cfg(feature = "gecko")] Grid, #[cfg(feature = "gecko")] InlineGrid, #[cfg(feature = "gecko")] Ruby, #[cfg(feature = "gecko")] RubyBase, #[cfg(feature = "gecko")] RubyBaseContainer, #[cfg(feature = "gecko")] RubyText, #[cfg(feature = "gecko")] RubyTextContainer, #[cfg(feature = "gecko")] Contents, #[cfg(feature = "gecko")] WebkitBox, #[cfg(feature = "gecko")] WebkitInlineBox, #[cfg(feature = "gecko")] #[parse(condition = "moz_box_display_values_enabled")] MozBox, #[cfg(feature = "gecko")] #[parse(condition = "moz_box_display_values_enabled")] MozInlineBox, #[cfg(feature = "gecko")] #[parse(condition = "moz_display_values_enabled")] MozGrid, #[cfg(feature = "gecko")] #[parse(condition = "moz_display_values_enabled")] MozInlineGrid, #[cfg(feature = "gecko")] #[parse(condition = "moz_display_values_enabled")] MozGridGroup, #[cfg(feature = "gecko")] #[parse(condition = "moz_display_values_enabled")] MozGridLine, #[cfg(feature = "gecko")] #[parse(condition = "moz_display_values_enabled")] MozStack, #[cfg(feature = "gecko")] #[parse(condition = "moz_display_values_enabled")] MozInlineStack, #[cfg(feature = "gecko")] #[parse(condition = "moz_display_values_enabled")] MozDeck, #[cfg(feature = "gecko")] #[parse(condition = "moz_display_values_enabled")] MozGroupbox, #[cfg(feature = "gecko")] #[parse(condition = "moz_display_values_enabled")] MozPopup, } impl Display { /// The initial display value. #[inline] pub fn inline() -> Self { Display::Inline } /// Returns whether this "display" value is the display of a flex or /// grid container. /// /// This is used to implement various style fixups. pub fn is_item_container(&self) -> bool { match *self { Display::Flex | Display::InlineFlex => true, #[cfg(feature = "gecko")] Display::Grid | Display::InlineGrid => true, _ => false, } } /// Returns whether an element with this display type is a line /// participant, which means it may lay its children on the same /// line as itself. pub fn is_line_participant(&self) -> bool { match *self { Display::Inline => true, #[cfg(feature = "gecko")] Display::Contents | Display::Ruby | Display::RubyBaseContainer => true, _ => false, } } /// Returns whether this "display" value is one of the types for /// ruby. #[cfg(feature = "gecko")] pub fn is_ruby_type(&self) -> bool { matches!( *self, Display::Ruby | Display::RubyBase | Display::RubyText | Display::RubyBaseContainer | Display::RubyTextContainer ) } /// Returns whether this "display" value is a ruby level container. #[cfg(feature = "gecko")] pub fn is_ruby_level_container(&self) -> bool { matches!( *self, Display::RubyBaseContainer | Display::RubyTextContainer ) } /// Convert this display into an equivalent block display. /// /// Also used for style adjustments. pub fn equivalent_block_display(&self, _is_root_element: bool) -> Self { match *self { // Values that have a corresponding block-outside version. Display::InlineTable => Display::Table, Display::InlineFlex => Display::Flex, #[cfg(feature = "gecko")] Display::InlineGrid => Display::Grid, #[cfg(feature = "gecko")] Display::WebkitInlineBox => Display::WebkitBox, // Special handling for contents and list-item on the root // element for Gecko. #[cfg(feature = "gecko")] Display::Contents | Display::ListItem if _is_root_element => Display::Block, // These are not changed by blockification. Display::None | Display::Block | Display::Flex | Display::ListItem | Display::Table => { *self }, #[cfg(feature = "gecko")] Display::Contents | Display::FlowRoot | Display::Grid | Display::WebkitBox => *self, // Everything else becomes block. _ => Display::Block, } } /// Convert this display into an inline-outside display. /// /// Ideally it should implement spec: https://drafts.csswg.org/css-display/#inlinify /// but the spec isn't stable enough, so we copy what Gecko does for now. #[cfg(feature = "gecko")] pub fn inlinify(&self) -> Self { match *self { Display::Block | Display::FlowRoot => Display::InlineBlock, Display::Table => Display::InlineTable, Display::Flex => Display::InlineFlex, Display::Grid => Display::InlineGrid, // XXX bug 1105868 this should probably be InlineListItem: Display::ListItem => Display::Inline, Display::MozBox => Display::MozInlineBox, Display::MozStack => Display::MozInlineStack, Display::WebkitBox => Display::WebkitInlineBox, other => other, } } /// Returns true if the value is `Contents` #[inline] pub fn is_contents(&self) -> bool { match *self { #[cfg(feature = "gecko")] Display::Contents => true, _ => false, } } /// Returns true if the value is `None` #[inline] pub fn is_none(&self) -> bool { *self == Display::None } } /// A specified value for the `vertical-align` property. pub type VerticalAlign = GenericVerticalAlign; impl Parse for VerticalAlign { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { if let Ok(lp) = input.try(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::Yes)) { return Ok(GenericVerticalAlign::Length(lp)); } try_match_ident_ignore_ascii_case! { input, "baseline" => Ok(GenericVerticalAlign::Baseline), "sub" => Ok(GenericVerticalAlign::Sub), "super" => Ok(GenericVerticalAlign::Super), "top" => Ok(GenericVerticalAlign::Top), "text-top" => Ok(GenericVerticalAlign::TextTop), "middle" => Ok(GenericVerticalAlign::Middle), "bottom" => Ok(GenericVerticalAlign::Bottom), "text-bottom" => Ok(GenericVerticalAlign::TextBottom), #[cfg(feature = "gecko")] "-moz-middle-with-baseline" => { Ok(GenericVerticalAlign::MozMiddleWithBaseline) }, } } } /// https://drafts.csswg.org/css-animations/#animation-iteration-count pub type AnimationIterationCount = GenericAnimationIterationCount; impl Parse for AnimationIterationCount { fn parse<'i, 't>( context: &ParserContext, input: &mut ::cssparser::Parser<'i, 't>, ) -> Result> { if input .try(|input| input.expect_ident_matching("infinite")) .is_ok() { return Ok(GenericAnimationIterationCount::Infinite); } let number = Number::parse_non_negative(context, input)?; Ok(GenericAnimationIterationCount::Number(number)) } } impl AnimationIterationCount { /// Returns the value `1.0`. #[inline] pub fn one() -> Self { GenericAnimationIterationCount::Number(Number::new(1.0)) } } /// A value for the `animation-name` property. #[derive( Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem, )] #[value_info(other_values = "none")] pub struct AnimationName(pub Option); impl AnimationName { /// Get the name of the animation as an `Atom`. pub fn as_atom(&self) -> Option<&Atom> { self.0.as_ref().map(|n| n.as_atom()) } /// Returns the `none` value. pub fn none() -> Self { AnimationName(None) } } impl ToCss for AnimationName { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { match self.0 { Some(ref name) => name.to_css(dest), None => dest.write_str("none"), } } } impl Parse for AnimationName { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { if let Ok(name) = input.try(|input| KeyframesName::parse(context, input)) { return Ok(AnimationName(Some(name))); } input.expect_ident_matching("none")?; Ok(AnimationName(None)) } } /// https://drafts.csswg.org/css-scroll-snap-1/#snap-axis #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive( Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum ScrollSnapAxis { X, Y, Block, Inline, Both, } /// https://drafts.csswg.org/css-scroll-snap-1/#snap-strictness #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive( Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum ScrollSnapStrictness { #[css(skip)] None, // Used to represent scroll-snap-type: none. It's not parsed. Mandatory, Proximity, } /// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive( Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem, )] #[repr(C)] pub struct ScrollSnapType { axis: ScrollSnapAxis, strictness: ScrollSnapStrictness, } impl ScrollSnapType { /// Returns `none`. #[inline] pub fn none() -> Self { Self { axis: ScrollSnapAxis::Both, strictness: ScrollSnapStrictness::None, } } } impl Parse for ScrollSnapType { /// none | [ x | y | block | inline | both ] [ mandatory | proximity ]? fn parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { if input .try(|input| input.expect_ident_matching("none")) .is_ok() { return Ok(ScrollSnapType::none()); } let axis = ScrollSnapAxis::parse(input)?; let strictness = input .try(ScrollSnapStrictness::parse) .unwrap_or(ScrollSnapStrictness::Proximity); Ok(Self { axis, strictness }) } } impl ToCss for ScrollSnapType { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { if self.strictness == ScrollSnapStrictness::None { return dest.write_str("none"); } self.axis.to_css(dest)?; if self.strictness != ScrollSnapStrictness::Proximity { dest.write_str(" ")?; self.strictness.to_css(dest)?; } Ok(()) } } /// Specified value of scroll-snap-align keyword value. #[allow(missing_docs)] #[derive( Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum ScrollSnapAlignKeyword { None, Start, End, Center, } /// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align #[allow(missing_docs)] #[derive( Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem, )] #[repr(C)] pub struct ScrollSnapAlign { block: ScrollSnapAlignKeyword, inline: ScrollSnapAlignKeyword, } impl ScrollSnapAlign { /// Returns `none`. #[inline] pub fn none() -> Self { ScrollSnapAlign { block: ScrollSnapAlignKeyword::None, inline: ScrollSnapAlignKeyword::None, } } } impl Parse for ScrollSnapAlign { /// [ none | start | end | center ]{1,2} fn parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { let block = ScrollSnapAlignKeyword::parse(input)?; let inline = input.try(ScrollSnapAlignKeyword::parse).unwrap_or(block); Ok(ScrollSnapAlign { block, inline }) } } impl ToCss for ScrollSnapAlign { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { self.block.to_css(dest)?; if self.block != self.inline { dest.write_str(" ")?; self.inline.to_css(dest)?; } Ok(()) } } #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive( Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum OverscrollBehavior { Auto, Contain, None, } #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive( Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum OverflowAnchor { Auto, None, } #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive( Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum OverflowClipBox { PaddingBox, ContentBox, } #[derive( Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] /// Provides a rendering hint to the user agent, /// stating what kinds of changes the author expects /// to perform on the element /// /// pub enum WillChange { /// Expresses no particular intent Auto, /// #[css(comma)] AnimateableFeatures { /// The features that are supposed to change. #[css(iterable)] features: Box<[CustomIdent]>, /// A bitfield with the kind of change that the value will create, based /// on the above field. #[css(skip)] bits: WillChangeBits, }, } impl WillChange { #[inline] /// Get default value of `will-change` as `auto` pub fn auto() -> WillChange { WillChange::Auto } } bitflags! { /// The change bits that we care about. #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)] #[repr(C)] pub struct WillChangeBits: u8 { /// Whether the stacking context will change. const STACKING_CONTEXT = 1 << 0; /// Whether `transform` will change. const TRANSFORM = 1 << 1; /// Whether `scroll-position` will change. const SCROLL = 1 << 2; /// Whether `opacity` will change. const OPACITY = 1 << 3; /// Fixed pos containing block. const FIXPOS_CB = 1 << 4; /// Abs pos containing block. const ABSPOS_CB = 1 << 5; } } fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits { let mut flags = match longhand { LonghandId::Opacity => WillChangeBits::OPACITY, LonghandId::Transform => WillChangeBits::TRANSFORM, _ => WillChangeBits::empty(), }; let property_flags = longhand.flags(); if property_flags.contains(PropertyFlags::CREATES_STACKING_CONTEXT) { flags |= WillChangeBits::STACKING_CONTEXT; } if property_flags.contains(PropertyFlags::FIXPOS_CB) { flags |= WillChangeBits::FIXPOS_CB; } if property_flags.contains(PropertyFlags::ABSPOS_CB) { flags |= WillChangeBits::ABSPOS_CB; } flags } fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits { let id = match PropertyId::parse_ignoring_rule_type(ident, context) { Ok(id) => id, Err(..) => return WillChangeBits::empty(), }; match id.as_shorthand() { Ok(shorthand) => shorthand .longhands() .fold(WillChangeBits::empty(), |flags, p| { flags | change_bits_for_longhand(p) }), Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand), Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(), } } impl Parse for WillChange { /// auto | # fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { if input .try(|input| input.expect_ident_matching("auto")) .is_ok() { return Ok(WillChange::Auto); } let mut bits = WillChangeBits::empty(); let custom_idents = input.parse_comma_separated(|i| { let location = i.current_source_location(); let parser_ident = i.expect_ident()?; let ident = CustomIdent::from_ident( location, parser_ident, &["will-change", "none", "all", "auto"], )?; if ident.0 == atom!("scroll-position") { bits |= WillChangeBits::SCROLL; } else { bits |= change_bits_for_maybe_property(&parser_ident, context); } Ok(ident) })?; Ok(WillChange::AnimateableFeatures { features: custom_idents.into_boxed_slice(), bits, }) } } bitflags! { /// Values for the `touch-action` property. #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)] /// These constants match Gecko's `NS_STYLE_TOUCH_ACTION_*` constants. #[value_info(other_values = "auto,none,manipulation,pan-x,pan-y")] #[repr(C)] pub struct TouchAction: u8 { /// `none` variant const NONE = 1 << 0; /// `auto` variant const AUTO = 1 << 1; /// `pan-x` variant const PAN_X = 1 << 2; /// `pan-y` variant const PAN_Y = 1 << 3; /// `manipulation` variant const MANIPULATION = 1 << 4; } } impl TouchAction { #[inline] /// Get default `touch-action` as `auto` pub fn auto() -> TouchAction { TouchAction::AUTO } } impl ToCss for TouchAction { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { match *self { TouchAction::NONE => dest.write_str("none"), TouchAction::AUTO => dest.write_str("auto"), TouchAction::MANIPULATION => dest.write_str("manipulation"), _ if self.contains(TouchAction::PAN_X | TouchAction::PAN_Y) => { dest.write_str("pan-x pan-y") }, _ if self.contains(TouchAction::PAN_X) => dest.write_str("pan-x"), _ if self.contains(TouchAction::PAN_Y) => dest.write_str("pan-y"), _ => panic!("invalid touch-action value"), } } } impl Parse for TouchAction { fn parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { try_match_ident_ignore_ascii_case! { input, "auto" => Ok(TouchAction::AUTO), "none" => Ok(TouchAction::NONE), "manipulation" => Ok(TouchAction::MANIPULATION), "pan-x" => { if input.try(|i| i.expect_ident_matching("pan-y")).is_ok() { Ok(TouchAction::PAN_X | TouchAction::PAN_Y) } else { Ok(TouchAction::PAN_X) } }, "pan-y" => { if input.try(|i| i.expect_ident_matching("pan-x")).is_ok() { Ok(TouchAction::PAN_X | TouchAction::PAN_Y) } else { Ok(TouchAction::PAN_Y) } }, } } } bitflags! { #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)] #[value_info(other_values = "none,strict,content,size,layout,paint")] #[repr(C)] /// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property pub struct Contain: u8 { /// `none` variant, just for convenience. const NONE = 0; /// 'size' variant, turns on size containment const SIZE = 1 << 0; /// `layout` variant, turns on layout containment const LAYOUT = 1 << 1; /// `paint` variant, turns on paint containment const PAINT = 1 << 2; /// `strict` variant, turns on all types of containment const STRICT = 1 << 3; /// 'content' variant, turns on layout and paint containment const CONTENT = 1 << 4; /// variant with all the bits that contain: strict turns on const STRICT_BITS = Contain::LAYOUT.bits | Contain::PAINT.bits | Contain::SIZE.bits; /// variant with all the bits that contain: content turns on const CONTENT_BITS = Contain::LAYOUT.bits | Contain::PAINT.bits; } } impl ToCss for Contain { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { if self.is_empty() { return dest.write_str("none"); } if self.contains(Contain::STRICT) { return dest.write_str("strict"); } if self.contains(Contain::CONTENT) { return dest.write_str("content"); } let mut has_any = false; macro_rules! maybe_write_value { ($ident:path => $str:expr) => { if self.contains($ident) { if has_any { dest.write_str(" ")?; } has_any = true; dest.write_str($str)?; } }; } maybe_write_value!(Contain::SIZE => "size"); maybe_write_value!(Contain::LAYOUT => "layout"); maybe_write_value!(Contain::PAINT => "paint"); debug_assert!(has_any); Ok(()) } } impl Parse for Contain { /// none | strict | content | [ size || layout || paint ] fn parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { let mut result = Contain::empty(); while let Ok(name) = input.try(|i| i.expect_ident_cloned()) { let flag = match_ignore_ascii_case! { &name, "size" => Some(Contain::SIZE), "layout" => Some(Contain::LAYOUT), "paint" => Some(Contain::PAINT), "strict" if result.is_empty() => return Ok(Contain::STRICT | Contain::STRICT_BITS), "content" if result.is_empty() => return Ok(Contain::CONTENT | Contain::CONTENT_BITS), "none" if result.is_empty() => return Ok(result), _ => None }; let flag = match flag { Some(flag) if !result.contains(flag) => flag, _ => { return Err( input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name)) ); }, }; result.insert(flag); } if !result.is_empty() { Ok(result) } else { Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } } } /// A specified value for the `perspective` property. pub type Perspective = GenericPerspective; /// 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, )] pub enum TransitionProperty { /// A shorthand. Shorthand(ShorthandId), /// A longhand transitionable property. Longhand(LonghandId), /// A custom property. Custom(CustomPropertyName), /// 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, { use crate::values::serialize_atom_name; match *self { TransitionProperty::Shorthand(ref s) => s.to_css(dest), TransitionProperty::Longhand(ref l) => l.to_css(dest), TransitionProperty::Custom(ref name) => { dest.write_str("--")?; 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(..) => { return Ok(TransitionProperty::Unsupported(CustomIdent::from_ident( location, ident, &["none"], )?)); }, }; Ok(match id.as_shorthand() { Ok(s) => TransitionProperty::Shorthand(s), Err(longhand_or_custom) => match longhand_or_custom { PropertyDeclarationId::Longhand(id) => TransitionProperty::Longhand(id), PropertyDeclarationId::Custom(custom) => TransitionProperty::Custom(custom.clone()), }, }) } } 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 `all`. #[inline] pub fn all() -> Self { TransitionProperty::Shorthand(ShorthandId::All) } /// Convert TransitionProperty to nsCSSPropertyID. #[cfg(feature = "gecko")] pub fn to_nscsspropertyid( &self, ) -> Result { Ok(match *self { TransitionProperty::Shorthand(ShorthandId::All) => { crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_all_properties }, TransitionProperty::Shorthand(ref id) => id.to_nscsspropertyid(), TransitionProperty::Longhand(ref id) => id.to_nscsspropertyid(), TransitionProperty::Custom(..) | TransitionProperty::Unsupported(..) => return Err(()), }) } } #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive( Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, )] /// https://drafts.csswg.org/css-box/#propdef-float pub enum Float { Left, Right, None, // https://drafts.csswg.org/css-logical-props/#float-clear InlineStart, InlineEnd, } #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive( Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, )] /// https://drafts.csswg.org/css-box/#propdef-clear pub enum Clear { None, Left, Right, Both, // https://drafts.csswg.org/css-logical-props/#float-clear InlineStart, InlineEnd, } /// https://drafts.csswg.org/css-ui/#propdef-resize #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive( Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, )] pub enum Resize { None, Both, Horizontal, Vertical, // https://drafts.csswg.org/css-logical-1/#resize Inline, Block, } /// The value for the `appearance` property. /// /// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance #[allow(missing_docs)] #[derive( Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToComputedValue, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum Appearance { /// No appearance at all. None, /// A typical dialog button. Button, /// Various arrows that go in buttons #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] ButtonArrowDown, #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] ButtonArrowNext, #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] ButtonArrowPrevious, #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] ButtonArrowUp, /// A rectangular button that contains complex content /// like images (e.g. HTML