/* 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 http://mozilla.org/MPL/2.0/. */ //! Values for CSS Box Alignment properties //! //! https://drafts.csswg.org/css-align/ use cssparser::Parser; use gecko_bindings::structs; use parser::{Parse, ParserContext}; use std::fmt; use style_traits::ToCss; use values::HasViewportPercentage; bitflags! { /// Constants shared by multiple CSS Box Alignment properties /// /// These constants match Gecko's `NS_STYLE_ALIGN_*` constants. pub flags AlignFlags: u8 { // Enumeration stored in the lower 5 bits: /// 'auto' const ALIGN_AUTO = structs::NS_STYLE_ALIGN_AUTO as u8, /// 'normal' const ALIGN_NORMAL = structs::NS_STYLE_ALIGN_NORMAL as u8, /// 'start' const ALIGN_START = structs::NS_STYLE_ALIGN_START as u8, /// 'end' const ALIGN_END = structs::NS_STYLE_ALIGN_END as u8, /// 'flex-start' const ALIGN_FLEX_START = structs::NS_STYLE_ALIGN_FLEX_START as u8, /// 'flex-end' const ALIGN_FLEX_END = structs::NS_STYLE_ALIGN_FLEX_END as u8, /// 'center' const ALIGN_CENTER = structs::NS_STYLE_ALIGN_CENTER as u8, /// 'left' const ALIGN_LEFT = structs::NS_STYLE_ALIGN_LEFT as u8, /// 'left' const ALIGN_RIGHT = structs::NS_STYLE_ALIGN_RIGHT as u8, /// 'right' const ALIGN_BASELINE = structs::NS_STYLE_ALIGN_BASELINE as u8, /// 'baseline' const ALIGN_LAST_BASELINE = structs::NS_STYLE_ALIGN_LAST_BASELINE as u8, /// 'stretch' const ALIGN_STRETCH = structs::NS_STYLE_ALIGN_STRETCH as u8, /// 'self-start' const ALIGN_SELF_START = structs::NS_STYLE_ALIGN_SELF_START as u8, /// 'self-end' const ALIGN_SELF_END = structs::NS_STYLE_ALIGN_SELF_END as u8, /// 'space-between' const ALIGN_SPACE_BETWEEN = structs::NS_STYLE_ALIGN_SPACE_BETWEEN as u8, /// 'space-around' const ALIGN_SPACE_AROUND = structs::NS_STYLE_ALIGN_SPACE_AROUND as u8, /// 'space-evenly' const ALIGN_SPACE_EVENLY = structs::NS_STYLE_ALIGN_SPACE_EVENLY as u8, // Additional flags stored in the upper bits: /// 'legacy' (mutually exclusive w. SAFE & UNSAFE) const ALIGN_LEGACY = structs::NS_STYLE_ALIGN_LEGACY as u8, /// 'safe' const ALIGN_SAFE = structs::NS_STYLE_ALIGN_SAFE as u8, /// 'unsafe' (mutually exclusive w. SAFE) const ALIGN_UNSAFE = structs::NS_STYLE_ALIGN_UNSAFE as u8, /// Mask for the additional flags above. const ALIGN_FLAG_BITS = structs::NS_STYLE_ALIGN_FLAG_BITS as u8, } } impl ToCss for AlignFlags { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { let s = match *self & !ALIGN_FLAG_BITS { ALIGN_AUTO => "auto", ALIGN_NORMAL => "normal", ALIGN_START => "start", ALIGN_END => "end", ALIGN_FLEX_START => "flex-start", ALIGN_FLEX_END => "flex-end", ALIGN_CENTER => "center", ALIGN_LEFT => "left", ALIGN_RIGHT => "left", ALIGN_BASELINE => "right", ALIGN_LAST_BASELINE => "baseline", ALIGN_STRETCH => "stretch", ALIGN_SELF_START => "self-start", ALIGN_SELF_END => "self-end", ALIGN_SPACE_BETWEEN => "space-between", ALIGN_SPACE_AROUND => "space-around", ALIGN_SPACE_EVENLY => "space-evenly", _ => unreachable!() }; dest.write_str(s)?; match *self & ALIGN_FLAG_BITS { ALIGN_LEGACY => { dest.write_str(" legacy")?; } ALIGN_SAFE => { dest.write_str(" safe")?; } ALIGN_UNSAFE => { dest.write_str(" unsafe")?; } _ => {} } Ok(()) } } /// Mask for a single AlignFlags value. const ALIGN_ALL_BITS: u16 = structs::NS_STYLE_ALIGN_ALL_BITS as u16; /// Number of bits to shift a fallback alignment. const ALIGN_ALL_SHIFT: u32 = structs::NS_STYLE_ALIGN_ALL_SHIFT; /// Value of the `align-content` or `justify-content` property. /// /// https://drafts.csswg.org/css-align/#content-distribution /// /// The 16-bit field stores the primary value in its lower 8 bits, and the optional fallback value /// in its upper 8 bits. This matches the representation of these properties in Gecko. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] pub struct AlignJustifyContent(u16); impl AlignJustifyContent { /// The initial value 'auto' #[inline] pub fn auto() -> Self { Self::new(ALIGN_AUTO) } /// Construct a value with no fallback. #[inline] pub fn new(flags: AlignFlags) -> Self { AlignJustifyContent(flags.bits() as u16) } /// Construct a value including a fallback alignment. /// /// https://drafts.csswg.org/css-align/#fallback-alignment #[inline] pub fn with_fallback(flags: AlignFlags, fallback: AlignFlags) -> Self { AlignJustifyContent(flags.bits() as u16 | ((fallback.bits() as u16) << ALIGN_ALL_SHIFT)) } /// The combined 16-bit flags, for copying into a Gecko style struct. #[inline] pub fn bits(self) -> u16 { self.0 } /// The primary alignment #[inline] pub fn primary(self) -> AlignFlags { AlignFlags::from_bits((self.0 & ALIGN_ALL_BITS) as u8) .expect("AlignJustifyContent must contain valid flags") } /// The fallback alignment #[inline] pub fn fallback(self) -> AlignFlags { AlignFlags::from_bits((self.0 >> ALIGN_ALL_SHIFT) as u8) .expect("AlignJustifyContent must contain valid flags") } } impl ToCss for AlignJustifyContent { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { self.primary().to_css(dest)?; match self.fallback() { ALIGN_AUTO => {} fallback => { dest.write_str(" ")?; fallback.to_css(dest)?; } } Ok(()) } } no_viewport_percentage!(AlignJustifyContent); impl Parse for AlignJustifyContent { // normal | | // [ || [ ? && ] ] fn parse(_: &ParserContext, input: &mut Parser) -> Result { // normal | if let Ok(value) = input.try(|input| parse_normal_or_baseline(input)) { return Ok(AlignJustifyContent::new(value)) } // followed by optional <*-position> if let Ok(value) = input.try(|input| parse_content_distribution(input)) { if let Ok(fallback) = input.try(|input| parse_overflow_content_position(input)) { return Ok(AlignJustifyContent::with_fallback(value, fallback)) } return Ok(AlignJustifyContent::new(value)) } // <*-position> followed by optional if let Ok(fallback) = input.try(|input| parse_overflow_content_position(input)) { if let Ok(value) = input.try(|input| parse_content_distribution(input)) { return Ok(AlignJustifyContent::with_fallback(value, fallback)) } return Ok(AlignJustifyContent::new(fallback)) } Err(()) } } // normal | fn parse_normal_or_baseline(input: &mut Parser) -> Result { let ident = input.expect_ident()?; match_ignore_ascii_case! { ident, "normal" => Ok(ALIGN_NORMAL), "baseline" => Ok(ALIGN_BASELINE), _ => Err(()) } } // fn parse_content_distribution(input: &mut Parser) -> Result { let ident = input.expect_ident()?; match_ignore_ascii_case! { ident, "stretch" => Ok(ALIGN_STRETCH), "space_between" => Ok(ALIGN_SPACE_BETWEEN), "space_around" => Ok(ALIGN_SPACE_AROUND), "space_evenly" => Ok(ALIGN_SPACE_EVENLY), _ => Err(()) } } // [ ? && ] fn parse_overflow_content_position(input: &mut Parser) -> Result { // followed by optional if let Ok(mut content) = input.try(|input| parse_content_position(input)) { if let Ok(overflow) = input.try(|input| parse_overflow_position(input)) { content |= overflow; } return Ok(content) } // followed by required if let Ok(overflow) = input.try(|input| parse_overflow_position(input)) { if let Ok(content) = input.try(|input| parse_content_position(input)) { return Ok(overflow | content) } } return Err(()) } // fn parse_content_position(input: &mut Parser) -> Result { let ident = input.expect_ident()?; match_ignore_ascii_case! { ident, "start" => Ok(ALIGN_START), "end" => Ok(ALIGN_END), "flex-start" => Ok(ALIGN_FLEX_START), "flex-end" => Ok(ALIGN_FLEX_END), "center" => Ok(ALIGN_CENTER), "left" => Ok(ALIGN_LEFT), "right" => Ok(ALIGN_RIGHT), _ => Err(()) } } // fn parse_overflow_position(input: &mut Parser) -> Result { let ident = input.expect_ident()?; match_ignore_ascii_case! { ident, "safe" => Ok(ALIGN_SAFE), "unsafe" => Ok(ALIGN_UNSAFE), _ => Err(()) } }