diff --git a/components/style/properties/longhand/background.mako.rs b/components/style/properties/longhand/background.mako.rs index adc7e44e0a9..7c49ea4ea5a 100644 --- a/components/style/properties/longhand/background.mako.rs +++ b/components/style/properties/longhand/background.mako.rs @@ -112,11 +112,16 @@ ${helpers.predefined_type("background-color", "CSSColor", #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { use values::specified::Percentage; + use values::specified::position::{HorizontalPosition, VerticalPosition}; Position { - horiz_keyword: None, - horiz_position: Some(specified::LengthOrPercentage::Percentage(Percentage(0.0))), - vert_keyword: None, - vert_position: Some(specified::LengthOrPercentage::Percentage(Percentage(0.0))), + horizontal: HorizontalPosition { + keyword: None, + position: Some(specified::LengthOrPercentage::Percentage(Percentage(0.0))), + }, + vertical: VerticalPosition { + keyword: None, + position: Some(specified::LengthOrPercentage::Percentage(Percentage(0.0))), + }, } } diff --git a/components/style/values/computed/position.rs b/components/style/values/computed/position.rs index d2a561639f4..253bccdc95b 100644 --- a/components/style/values/computed/position.rs +++ b/components/style/values/computed/position.rs @@ -26,3 +26,19 @@ impl ToCss for Position { Ok(()) } } + +pub struct HorizontalPosition(pub LengthOrPercentage); + +impl ToCss for HorizontalPosition { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.0.to_css(dest) + } +} + +pub struct VerticalPosition(pub LengthOrPercentage); + +impl ToCss for VerticalPosition { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.0.to_css(dest) + } +} diff --git a/components/style/values/specified/basic_shape.rs b/components/style/values/specified/basic_shape.rs index b754eefde69..a943d4ec7da 100644 --- a/components/style/values/specified/basic_shape.rs +++ b/components/style/values/specified/basic_shape.rs @@ -15,7 +15,7 @@ use style_traits::ToCss; use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue}; use values::computed::basic_shape as computed_basic_shape; use values::specified::{BorderRadiusSize, LengthOrPercentage, Percentage}; -use values::specified::position::{Keyword, Position}; +use values::specified::position::{Keyword, Position, HorizontalPosition, VerticalPosition}; use values::specified::url::SpecifiedUrl; /// A shape source, for some reference box @@ -313,6 +313,7 @@ fn serialize_basicshape_position(position: &Position, dest: &mut W) }, Some(Keyword::Left) | Some(Keyword::Top) | None => pc, Some(Keyword::Right) | Some(Keyword::Bottom) => Percentage(1.0 - pc.0), + _ => return None, }; Some(LengthOrPercentage::Percentage(percent)) } @@ -336,8 +337,8 @@ fn serialize_basicshape_position(position: &Position, dest: &mut W) replace_with_percent(y).to_css(dest) } - match (position.horiz_keyword, position.horiz_position, - position.vert_keyword, position.vert_position) { + match (position.horizontal.keyword, position.horizontal.position, + position.vertical.keyword, position.vertical.position) { (Some(hk), None, Some(vk), None) => { // two keywords: serialize as two lengths serialize_position_pair(hk.to_length_or_percentage(), @@ -385,10 +386,14 @@ impl Circle { } else { // Defaults to origin Position { - horiz_keyword: Some(Keyword::Center), - horiz_position: None, - vert_keyword: Some(Keyword::Center), - vert_position: None, + horizontal: HorizontalPosition { + keyword: Some(Keyword::Center), + position: None, + }, + vertical: VerticalPosition { + keyword: Some(Keyword::Center), + position: None, + }, } }; Ok(Circle { @@ -462,10 +467,14 @@ impl Ellipse { } else { // Defaults to origin Position { - horiz_keyword: Some(Keyword::Center), - horiz_position: None, - vert_keyword: Some(Keyword::Center), - vert_position: None, + horizontal: HorizontalPosition { + keyword: Some(Keyword::Center), + position: None, + }, + vertical: VerticalPosition { + keyword: Some(Keyword::Center), + position: None, + }, } }; Ok(Ellipse { diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs index ed9813cf02c..d572e076f6f 100644 --- a/components/style/values/specified/position.rs +++ b/components/style/values/specified/position.rs @@ -21,31 +21,29 @@ use values::specified::{LengthOrPercentage, Percentage}; #[derive(Debug, Clone, PartialEq, Copy)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct Position { - pub horiz_keyword: Option, - pub horiz_position: Option, - pub vert_keyword: Option, - pub vert_position: Option, + pub horizontal: HorizontalPosition, + pub vertical: VerticalPosition, } impl ToCss for Position { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - let mut space_at_last = false; - if let Some(horiz_key) = self.horiz_keyword { + let mut space_present = false; + if let Some(horiz_key) = self.horizontal.keyword { try!(horiz_key.to_css(dest)); try!(dest.write_str(" ")); - space_at_last = true; + space_present = true; }; - if let Some(horiz_pos) = self.horiz_position { + if let Some(horiz_pos) = self.horizontal.position { try!(horiz_pos.to_css(dest)); try!(dest.write_str(" ")); - space_at_last = true; + space_present = true; }; - if let Some(vert_key) = self.vert_keyword { + if let Some(vert_key) = self.vertical.keyword { try!(vert_key.to_css(dest)); - space_at_last = false; + space_present = false; }; - if let Some(vert_pos) = self.vert_position { - if space_at_last == false { + if let Some(vert_pos) = self.vertical.position { + if space_present == false { try!(dest.write_str(" ")); } try!(vert_pos.to_css(dest)); @@ -56,38 +54,10 @@ impl ToCss for Position { impl HasViewportPercentage for Position { fn has_viewport_percentage(&self) -> bool { - let horiz_viewport = if let Some(horiz_pos) = self.horiz_position { - horiz_pos.has_viewport_percentage() - } else { - false - }; - - let vert_viewport = if let Some(vert_pos) = self.vert_position { - vert_pos.has_viewport_percentage() - } else { - false - }; - horiz_viewport || vert_viewport + self.horizontal.has_viewport_percentage() || self.vertical.has_viewport_percentage() } } -#[derive(Debug, Clone, PartialEq, Copy)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum Keyword { - Center, - Left, - Right, - Top, - Bottom, -} - -// http://dev.w3.org/csswg/css2/colors.html#propdef-background-position -#[derive(Clone, PartialEq, Copy)] -pub enum PositionComponent { - Length(LengthOrPercentage), - Keyword(Keyword), -} - impl Position { pub fn new(mut first_position: Option, mut second_position: Option, first_keyword: Option, second_keyword: Option) @@ -117,6 +87,12 @@ impl Position { (PositionCategory::LengthOrPercentage, PositionCategory::HorizontalKeyword) | (PositionCategory::VerticalKeyword, PositionCategory::LengthOrPercentage) => return Err(()), + // FIXME(canaltinova): Allow logical keywords for Position. They are not in current spec yet. + (PositionCategory::HorizontalLogicalKeyword, _) | + (PositionCategory::VerticalLogicalKeyword, _) | + (_, PositionCategory::HorizontalLogicalKeyword) | + (_, PositionCategory::VerticalLogicalKeyword) => return Err(()), + // Swap if both are keywords and vertical precedes horizontal. (PositionCategory::VerticalKeyword, PositionCategory::HorizontalKeyword) | (PositionCategory::VerticalKeyword, PositionCategory::OtherKeyword) | @@ -163,19 +139,27 @@ impl Position { }; Ok(Position { - horiz_keyword: horizontal_keyword, - horiz_position: first_position, - vert_keyword: vertical_keyword, - vert_position: second_position, + horizontal: HorizontalPosition { + keyword: horizontal_keyword, + position: first_position, + }, + vertical: VerticalPosition { + keyword: vertical_keyword, + position: second_position, + }, }) } pub fn center() -> Position { Position { - horiz_keyword: Some(Keyword::Center), - horiz_position: None, - vert_keyword: Some(Keyword::Center), - vert_position: None, + horizontal: HorizontalPosition { + keyword: Some(Keyword::Center), + position: None, + }, + vertical: VerticalPosition { + keyword: Some(Keyword::Center), + position: None, + }, } } } @@ -230,46 +214,342 @@ impl Parse for Position { } } +impl ToComputedValue for Position { + type ComputedValue = computed_position::Position; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_position::Position { + computed_position::Position { + horizontal: self.horizontal.to_computed_value(context).0, + vertical: self.vertical.to_computed_value(context).0, + } + } + + #[inline] + fn from_computed_value(computed: &computed_position::Position) -> Position { + Position { + horizontal: HorizontalPosition { + keyword: None, + position: Some(ToComputedValue::from_computed_value(&computed.horizontal)), + }, + vertical: VerticalPosition { + keyword: None, + position: Some(ToComputedValue::from_computed_value(&computed.vertical)), + }, + } + } +} + +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct HorizontalPosition { + pub keyword: Option, + pub position: Option, +} + +impl HasViewportPercentage for HorizontalPosition { + fn has_viewport_percentage(&self) -> bool { + if let Some(pos) = self.position { + pos.has_viewport_percentage() + } else { + false + } + } +} + +impl ToCss for HorizontalPosition { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let mut keyword_present = false; + if let Some(keyword) = self.keyword { + try!(keyword.to_css(dest)); + keyword_present = true; + }; + + if let Some(position) = self.position { + if keyword_present { + try!(dest.write_str(" ")); + } + try!(position.to_css(dest)); + }; + Ok(()) + } +} + +impl Parse for HorizontalPosition { + #[inline] + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + let first = try!(PositionComponent::parse(context, input)); + let second = input.try(|i| PositionComponent::parse(context, i)).ok(); + + let (keyword, position) = if let PositionCategory::LengthOrPercentage = category(first) { + // "length keyword?" + (second, Some(first)) + } else { + // "keyword length?" + (Some(first), second) + }; + + // Unwrapping and checking keyword. + let keyword = match keyword { + Some(PositionComponent::Keyword(key)) => { + match category(keyword.unwrap()) { + PositionCategory::VerticalKeyword | + PositionCategory::VerticalLogicalKeyword => return Err(()), + _ => Some(key), + } + }, + Some(_) => return Err(()), + None => None, + }; + + // Unwrapping and checking position. + let position = match position { + Some(PositionComponent::Length(pos)) => { + // "center " is not allowed + if let Some(Keyword::Center) = keyword { + return Err(()); + } + Some(pos) + }, + Some(_) => return Err(()), + None => None, + }; + + Ok(HorizontalPosition { + keyword: keyword, + position: position, + }) + } +} + +impl ToComputedValue for HorizontalPosition { + type ComputedValue = computed_position::HorizontalPosition; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_position::HorizontalPosition { + let keyword = self.keyword.unwrap_or(Keyword::Left); + + // Construct horizontal computed LengthOrPercentage + let horizontal = match keyword { + // FIXME(canaltinova): Support logical keywords. + Keyword::Right | Keyword::XEnd => { + if let Some(x) = self.position { + let (length, percentage) = match x { + LengthOrPercentage::Percentage(Percentage(y)) => (Au(0), Some(1.0 - y)), + LengthOrPercentage::Length(y) => (-y.to_computed_value(context), Some(1.0)), + _ => (Au(0), None), + }; + ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage { + length: length, + percentage: percentage + }) + } else { + ComputedLengthOrPercentage::Percentage(1.0) + } + }, + Keyword::Center => { + keyword.to_length_or_percentage().to_computed_value(context) + }, + _ => { + let horiz = self.position + .unwrap_or(LengthOrPercentage::Percentage(Percentage(0.0))); + horiz.to_computed_value(context) + }, + }; + + computed_position::HorizontalPosition(horizontal) + } + + #[inline] + fn from_computed_value(computed: &computed_position::HorizontalPosition) -> HorizontalPosition { + HorizontalPosition { + keyword: None, + position: Some(ToComputedValue::from_computed_value(&computed.0)), + } + } +} + +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct VerticalPosition { + pub keyword: Option, + pub position: Option, +} + +impl HasViewportPercentage for VerticalPosition { + fn has_viewport_percentage(&self) -> bool { + if let Some(pos) = self.position { + pos.has_viewport_percentage() + } else { + false + } + } +} + +impl ToCss for VerticalPosition { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let mut keyword_present = false; + if let Some(keyword) = self.keyword { + try!(keyword.to_css(dest)); + keyword_present = true; + }; + + if let Some(position) = self.position { + if keyword_present { + try!(dest.write_str(" ")); + } + try!(position.to_css(dest)); + }; + Ok(()) + } +} + +impl Parse for VerticalPosition { + #[inline] + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + let first = try!(PositionComponent::parse(context, input)); + let second = input.try(|i| PositionComponent::parse(context, i)).ok(); + + let (keyword, position) = if let PositionCategory::LengthOrPercentage = category(first) { + // "length keyword?" + (second, Some(first)) + } else { + // "keyword length?" + (Some(first), second) + }; + + // Unwrapping and checking keyword. + let keyword = match keyword { + Some(PositionComponent::Keyword(key)) => { + match category(keyword.unwrap()) { + PositionCategory::HorizontalKeyword | + PositionCategory::HorizontalLogicalKeyword => return Err(()), + _ => Some(key), + } + }, + Some(_) => return Err(()), + None => None, + }; + + // Unwrapping and checking position. + let position = match position { + Some(PositionComponent::Length(pos)) => { + // "center " is not allowed + if let Some(Keyword::Center) = keyword { + return Err(()); + } + Some(pos) + }, + Some(_) => return Err(()), + None => None, + }; + + Ok(VerticalPosition { + keyword: keyword, + position: position, + }) + } +} + +impl ToComputedValue for VerticalPosition { + type ComputedValue = computed_position::VerticalPosition; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_position::VerticalPosition { + let keyword = self.keyword.unwrap_or(Keyword::Left); + + // Construct vertical computed LengthOrPercentage + let vertical = match keyword { + // FIXME(canaltinova): Support logical keywords. + Keyword::Bottom | Keyword::YEnd => { + if let Some(x) = self.position { + let (length, percentage) = match x { + LengthOrPercentage::Percentage(Percentage(y)) => (Au(0), Some(1.0 - y)), + LengthOrPercentage::Length(y) => (-y.to_computed_value(context), Some(1.0)), + _ => (Au(0), None), + }; + ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage { + length: length, + percentage: percentage + }) + } else { + ComputedLengthOrPercentage::Percentage(1.0) + } + }, + Keyword::Center => { + keyword.to_length_or_percentage().to_computed_value(context) + }, + _ => { + let vert = self.position + .unwrap_or(LengthOrPercentage::Percentage(Percentage(0.0))); + vert.to_computed_value(context) + }, + }; + + computed_position::VerticalPosition(vertical) + } + + #[inline] + fn from_computed_value(computed: &computed_position::VerticalPosition) -> VerticalPosition { + VerticalPosition { + keyword: None, + position: Some(ToComputedValue::from_computed_value(&computed.0)), + } + } +} + +define_css_keyword_enum!(Keyword: + "center" => Center, + "left" => Left, + "right" => Right, + "top" => Top, + "bottom" => Bottom, + "x-start" => XStart, + "x-end" => XEnd, + "y-start" => YStart, + "y-end" => YEnd); + impl Keyword { pub fn to_length_or_percentage(self) -> LengthOrPercentage { match self { Keyword::Center => LengthOrPercentage::Percentage(Percentage(0.5)), Keyword::Left | Keyword::Top => LengthOrPercentage::Percentage(Percentage(0.0)), Keyword::Right | Keyword::Bottom => LengthOrPercentage::Percentage(Percentage(1.0)), + // FIXME(canaltinova): Support logical keywords + Keyword::XStart | Keyword::YStart => LengthOrPercentage::Percentage(Percentage(0.0)), + Keyword::XEnd | Keyword::YEnd => LengthOrPercentage::Percentage(Percentage(1.0)), } } } -impl ToCss for Keyword { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - Keyword::Center => try!(dest.write_str("center")), - Keyword::Left => try!(dest.write_str("left")), - Keyword::Right => try!(dest.write_str("right")), - Keyword::Top => try!(dest.write_str("top")), - Keyword::Bottom => try!(dest.write_str("bottom")), - } - Ok(()) - } -} - // Collapse `Position` into a few categories to simplify the above `match` expression. enum PositionCategory { HorizontalKeyword, VerticalKeyword, + HorizontalLogicalKeyword, + VerticalLogicalKeyword, OtherKeyword, LengthOrPercentage, } +// http://dev.w3.org/csswg/css2/colors.html#propdef-background-position +#[derive(Clone, PartialEq, Copy)] +pub enum PositionComponent { + Length(LengthOrPercentage), + Keyword(Keyword), +} + fn category(p: PositionComponent) -> PositionCategory { if let PositionComponent::Keyword(keyword) = p { match keyword { - Keyword::Left | - Keyword::Right => + Keyword::Left | Keyword::Right => PositionCategory::HorizontalKeyword, - Keyword::Top | - Keyword::Bottom => + Keyword::Top | Keyword::Bottom => PositionCategory::VerticalKeyword, + Keyword::XStart | Keyword::XEnd => + PositionCategory::HorizontalLogicalKeyword, + Keyword::YStart | Keyword::YEnd => + PositionCategory::VerticalLogicalKeyword, Keyword::Center => PositionCategory::OtherKeyword, } @@ -278,83 +558,6 @@ fn category(p: PositionComponent) -> PositionCategory { } } -impl ToComputedValue for Position { - type ComputedValue = computed_position::Position; - - #[inline] - fn to_computed_value(&self, context: &Context) -> computed_position::Position { - let horiz_keyword = self.horiz_keyword.unwrap_or(Keyword::Left); - let vert_keyword = self.vert_keyword.unwrap_or(Keyword::Top); - - // Construct horizontal computed LengthOrPercentage - let horizontal = match horiz_keyword { - Keyword::Right => { - if let Some(x) = self.horiz_position { - let (length, percentage) = match x { - LengthOrPercentage::Percentage(Percentage(y)) => (Au(0), Some(1.0 - y)), - LengthOrPercentage::Length(y) => (-y.to_computed_value(context), Some(1.0)), - _ => (Au(0), None), - }; - ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage { - length: length, - percentage: percentage - }) - } else { - ComputedLengthOrPercentage::Percentage(1.0) - } - }, - Keyword::Center => { - horiz_keyword.to_length_or_percentage().to_computed_value(context) - }, - _ => { - let horiz = self.horiz_position.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.0))); - horiz.to_computed_value(context) - }, - }; - - // Construct vertical computed LengthOrPercentage - let vertical = match vert_keyword { - Keyword::Bottom => { - if let Some(x) = self.vert_position { - let (length, percentage) = match x { - LengthOrPercentage::Percentage(Percentage(y)) => (Au(0), Some(1.0 - y)), - LengthOrPercentage::Length(y) => (-y.to_computed_value(context), Some(1.0)), - _ => (Au(0), None), - }; - ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage { - length: length, - percentage: percentage - }) - } else { - ComputedLengthOrPercentage::Percentage(1.0) - } - }, - Keyword::Center => { - vert_keyword.to_length_or_percentage().to_computed_value(context) - }, - _ => { - let vert = self.vert_position.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.0))); - vert.to_computed_value(context) - }, - }; - - computed_position::Position { - horizontal: horizontal, - vertical: vertical, - } - } - - #[inline] - fn from_computed_value(computed: &computed_position::Position) -> Position { - Position { - horiz_keyword: None, - horiz_position: Some(ToComputedValue::from_computed_value(&computed.horizontal)), - vert_keyword: None, - vert_position: Some(ToComputedValue::from_computed_value(&computed.vertical)), - } - } -} - impl HasViewportPercentage for PositionComponent { fn has_viewport_percentage(&self) -> bool { match *self { @@ -387,6 +590,10 @@ impl Parse for PositionComponent { "right" => Ok(PositionComponent::Keyword(Keyword::Right)), "top" => Ok(PositionComponent::Keyword(Keyword::Top)), "bottom" => Ok(PositionComponent::Keyword(Keyword::Bottom)), + "x-start" => Ok(PositionComponent::Keyword(Keyword::XStart)), + "x-end" => Ok(PositionComponent::Keyword(Keyword::XEnd)), + "y-start" => Ok(PositionComponent::Keyword(Keyword::YStart)), + "y-end" => Ok(PositionComponent::Keyword(Keyword::YEnd)), _ => Err(()) } }, diff --git a/tests/unit/style/parsing/position.rs b/tests/unit/style/parsing/position.rs index 5bb0d414510..34b7528a329 100644 --- a/tests/unit/style/parsing/position.rs +++ b/tests/unit/style/parsing/position.rs @@ -61,4 +61,79 @@ fn test_position() { assert!(parse(Position::parse, "top 10px bottom").is_err()); assert!(parse(Position::parse, "top 10px bottom 15%").is_err()); + // Logical keywords are not supported in Position yet + assert!(parse(Position::parse, "x-start").is_err()); + assert!(parse(Position::parse, "y-end").is_err()); + assert!(parse(Position::parse, "x-start y-end").is_err()); + assert!(parse(Position::parse, "x-end 10px").is_err()); + assert!(parse(Position::parse, "y-start 20px").is_err()); + assert!(parse(Position::parse, "x-start bottom 10%").is_err()); + assert!(parse(Position::parse, "left y-start 10%").is_err()); + assert!(parse(Position::parse, "x-start 20px y-end 10%").is_err()); +} + +#[test] +fn test_horizontal_position() { + // One value serializations. + assert_roundtrip_with_context!(HorizontalPosition::parse, "20px", "20px"); + assert_roundtrip_with_context!(HorizontalPosition::parse, "25%", "25%"); + assert_roundtrip_with_context!(HorizontalPosition::parse, "center", "center"); + assert_roundtrip_with_context!(HorizontalPosition::parse, "left", "left"); + assert_roundtrip_with_context!(HorizontalPosition::parse, "right", "right"); + assert_roundtrip_with_context!(HorizontalPosition::parse, "x-start", "x-start"); + assert_roundtrip_with_context!(HorizontalPosition::parse, "x-end", "x-end"); + + // Two value serializations. + assert_roundtrip_with_context!(HorizontalPosition::parse, "right 10px", "right 10px"); + assert_roundtrip_with_context!(HorizontalPosition::parse, "10px left", "left 10px"); + assert_roundtrip_with_context!(HorizontalPosition::parse, "x-end 20%", "x-end 20%"); + assert_roundtrip_with_context!(HorizontalPosition::parse, "20px x-start", "x-start 20px"); + + // Invalid horizontal positions. + assert!(parse(HorizontalPosition::parse, "top").is_err()); + assert!(parse(HorizontalPosition::parse, "bottom").is_err()); + assert!(parse(HorizontalPosition::parse, "y-start").is_err()); + assert!(parse(HorizontalPosition::parse, "y-end").is_err()); + assert!(parse(HorizontalPosition::parse, "20px y-end").is_err()); + assert!(parse(HorizontalPosition::parse, "y-end 20px ").is_err()); + assert!(parse(HorizontalPosition::parse, "bottom 20px").is_err()); + assert!(parse(HorizontalPosition::parse, "20px top").is_err()); + assert!(parse(HorizontalPosition::parse, "left center").is_err()); + assert!(parse(HorizontalPosition::parse, "bottom top").is_err()); + assert!(parse(HorizontalPosition::parse, "left top").is_err()); + assert!(parse(HorizontalPosition::parse, "left right").is_err()); + assert!(parse(HorizontalPosition::parse, "20px 30px").is_err()); +} + +#[test] +fn test_vertical_position() { + // One value serializations. + assert_roundtrip_with_context!(VerticalPosition::parse, "20px", "20px"); + assert_roundtrip_with_context!(VerticalPosition::parse, "25%", "25%"); + assert_roundtrip_with_context!(VerticalPosition::parse, "center", "center"); + assert_roundtrip_with_context!(VerticalPosition::parse, "top", "top"); + assert_roundtrip_with_context!(VerticalPosition::parse, "bottom", "bottom"); + assert_roundtrip_with_context!(VerticalPosition::parse, "y-start", "y-start"); + assert_roundtrip_with_context!(VerticalPosition::parse, "y-end", "y-end"); + + // Two value serializations. + assert_roundtrip_with_context!(VerticalPosition::parse, "bottom 10px", "bottom 10px"); + assert_roundtrip_with_context!(VerticalPosition::parse, "10px top", "top 10px"); + assert_roundtrip_with_context!(VerticalPosition::parse, "y-end 20%", "y-end 20%"); + assert_roundtrip_with_context!(VerticalPosition::parse, "20px y-start", "y-start 20px"); + + // Invalid vertical positions. + assert!(parse(VerticalPosition::parse, "left").is_err()); + assert!(parse(VerticalPosition::parse, "right").is_err()); + assert!(parse(VerticalPosition::parse, "x-start").is_err()); + assert!(parse(VerticalPosition::parse, "x-end").is_err()); + assert!(parse(VerticalPosition::parse, "20px x-end").is_err()); + assert!(parse(VerticalPosition::parse, "x-end 20px ").is_err()); + assert!(parse(VerticalPosition::parse, "left 20px").is_err()); + assert!(parse(VerticalPosition::parse, "20px right").is_err()); + assert!(parse(VerticalPosition::parse, "left center").is_err()); + assert!(parse(VerticalPosition::parse, "bottom top").is_err()); + assert!(parse(VerticalPosition::parse, "left top").is_err()); + assert!(parse(VerticalPosition::parse, "left right").is_err()); + assert!(parse(VerticalPosition::parse, "20px 30px").is_err()); } diff --git a/tests/unit/style/properties/serialization.rs b/tests/unit/style/properties/serialization.rs index c27fe00dc61..182f128faa0 100644 --- a/tests/unit/style/properties/serialization.rs +++ b/tests/unit/style/properties/serialization.rs @@ -692,7 +692,7 @@ mod shorthand_serialization { use style::properties::longhands::background_repeat as repeat; use style::properties::longhands::background_size as size; use style::values::specified::Image; - use style::values::specified::position::Position; + use style::values::specified::position::{HorizontalPosition, Position, VerticalPosition}; use super::*; macro_rules! single_vec_value_typedef { ($name:ident, $path:expr) => { @@ -733,10 +733,14 @@ mod shorthand_serialization { let position = single_vec_value_typedef!(position, Position { - horiz_keyword: None, - horiz_position: Some(LengthOrPercentage::Length(Length::from_px(7f32))), - vert_keyword: None, - vert_position: Some(LengthOrPercentage::Length(Length::from_px(4f32))) + horizontal: HorizontalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(7f32))), + }, + vertical: VerticalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(4f32))), + }, } ); @@ -787,10 +791,14 @@ mod shorthand_serialization { let position = single_vec_value_typedef!(position, Position { - horiz_keyword: None, - horiz_position: Some(LengthOrPercentage::Length(Length::from_px(7f32))), - vert_keyword: None, - vert_position: Some(LengthOrPercentage::Length(Length::from_px(4f32))) + horizontal: HorizontalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(7f32))), + }, + vertical: VerticalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(4f32))), + }, } ); @@ -840,10 +848,14 @@ mod shorthand_serialization { let position = single_vec_value_typedef!(position, Position { - horiz_keyword: None, - horiz_position: Some(LengthOrPercentage::Length(Length::from_px(0f32))), - vert_keyword: None, - vert_position: Some(LengthOrPercentage::Length(Length::from_px(0f32))) + horizontal: HorizontalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(0f32))), + }, + vertical: VerticalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(0f32))), + }, } ); @@ -881,7 +893,7 @@ mod shorthand_serialization { use style::properties::longhands::mask_repeat as repeat; use style::properties::longhands::mask_size as size; use style::values::specified::Image; - use style::values::specified::position::Position; + use style::values::specified::position::{HorizontalPosition, Position, VerticalPosition}; use super::*; macro_rules! single_vec_value_typedef { @@ -918,10 +930,14 @@ mod shorthand_serialization { let position = single_vec_value_typedef!(position, Position { - horiz_keyword: None, - horiz_position: Some(LengthOrPercentage::Length(Length::from_px(7f32))), - vert_keyword: None, - vert_position: Some(LengthOrPercentage::Length(Length::from_px(4f32))) + horizontal: HorizontalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(7f32))), + }, + vertical: VerticalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(4f32))), + }, } ); @@ -968,10 +984,14 @@ mod shorthand_serialization { let position = single_vec_value_typedef!(position, Position { - horiz_keyword: None, - horiz_position: Some(LengthOrPercentage::Length(Length::from_px(7f32))), - vert_keyword: None, - vert_position: Some(LengthOrPercentage::Length(Length::from_px(4f32))) + horizontal: HorizontalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(7f32))), + }, + vertical: VerticalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(4f32))), + }, } );