diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2016-12-15 12:15:06 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-12-15 12:15:06 -0800 |
commit | 5357f05ff785ee160d6b07de5d0e10aba199e578 (patch) | |
tree | 1fce6fc1ede31c0b58cfcee318c0872450e9e0c6 | |
parent | b1337da1143dfe4d494c39cdce78e1469c200ff5 (diff) | |
parent | ca85221525962dce862a67600654d41fd744cf36 (diff) | |
download | servo-5357f05ff785ee160d6b07de5d0e10aba199e578.tar.gz servo-5357f05ff785ee160d6b07de5d0e10aba199e578.zip |
Auto merge of #14509 - canaltinova:position, r=Manishearth
Implement background-position-x/y
<!-- Please describe your changes on the following line: -->
This is a WIP PR. Just HorizontalPosition / VerticalPosition implementations are complete. I would like to get early feedbacks about this architecture. Here's some architectural topics to consider:
- I created `HorizontalPosition` and `VerticalPosition` structs for this and used them in `Position` as well. We have decided to split `Keyword` enum, but we need them as unified for `PositionComponent` enum. So I didn't split but I can split it if we prefer to change PositionComponent as well.
- If we prefer Keyword enum like this, we can create a SubPosition(or something like this) instead of HorizontalPosition/VerticalPosition enums since only difference is 2 lines in `parse` functions. We can create a `parse_horizontal` and `parse_vertical` instead and a lot of code duplication can be cleared.
- I couldn't find a good way to use HorizontalPosition/VerticalPosition's parse functions in `Position`'s parse function. It is a bit more complicated. I'm open to suggestions :)
- I don't know much about logical keywords so do I need to do something different? I placed some comments where logical keywords are processing.
Any advice about these?
---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #14458 (github issue number if applicable).
<!-- Either: -->
- [X] There are tests for these changes
<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14509)
<!-- Reviewable:end -->
17 files changed, 848 insertions, 219 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index f2534729e5f..5f66f5f5259 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -755,11 +755,12 @@ impl FragmentDisplayListBuilding for Fragment { } }; - let position = *get_cyclic(&background.background_position.0, index); + let horiz_position = *get_cyclic(&background.background_position_x.0, index); + let vert_position = *get_cyclic(&background.background_position_y.0, index); // Use `background-position` to get the offset. - let horizontal_position = model::specified(position.horizontal, + let horizontal_position = model::specified(horiz_position.0, bounds.size.width - image_size.width); - let vertical_position = model::specified(position.vertical, + let vertical_position = model::specified(vert_position.0, bounds.size.height - image_size.height); // The anchor position for this background, based on both the background-attachment diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index 4314eca74f9..c32e9afddfc 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -38,6 +38,10 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-color; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundPosition; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-position; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundPositionX; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-position-x; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundPositionY; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-position-y; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundRepeat; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-repeat; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundImage; diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index f5b7585b32b..1fb133f92fd 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -1389,6 +1389,7 @@ fn static_assert() { } </%self:simple_image_array_property> + % if shorthand != "background": pub fn copy_${shorthand}_position_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; @@ -1424,7 +1425,7 @@ fn static_assert() { pub fn clone_${shorthand}_position(&self) -> longhands::${shorthand}_position::computed_value::T { use values::computed::position::Position; - longhands::background_position::computed_value::T( + longhands::${shorthand}_position::computed_value::T( self.gecko.${image_layers_field}.mLayers.iter() .take(self.gecko.${image_layers_field}.mPositionXCount as usize) .take(self.gecko.${image_layers_field}.mPositionYCount as usize) @@ -1453,6 +1454,7 @@ fn static_assert() { geckolayer.mPosition.mYPosition = servo.vertical.into(); } } + % endif <%self:simple_image_array_property name="size" shorthand="${shorthand}" field_name="mSize"> use gecko_bindings::structs::nsStyleImageLayers_Size_Dimension; @@ -1602,7 +1604,9 @@ fn static_assert() { background-image background-clip background-origin background-attachment background-size background-position - background-blend-mode""" %> + background-blend-mode + background-position-x + background-position-y""" %> <%self:impl_trait style_struct_name="Background" skip_longhands="${skip_background_longhands}" skip_additionals="*"> @@ -1640,6 +1644,56 @@ fn static_assert() { T::luminosity => structs::NS_STYLE_BLEND_LUMINOSITY as u8, } </%self:simple_image_array_property> + + % for orientation in [("x", "Horizontal"), ("y", "Vertical")]: + pub fn copy_background_position_${orientation[0]}_from(&mut self, other: &Self) { + use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; + + self.gecko.mImage.mPosition${orientation[0].upper()}Count + = cmp::min(1, other.gecko.mImage.mPosition${orientation[0].upper()}Count); + self.gecko.mImage.mLayers.mFirstElement.mPosition = + other.gecko.mImage.mLayers.mFirstElement.mPosition; + unsafe { + Gecko_EnsureImageLayersLength(&mut self.gecko.mImage, + other.gecko.mImage.mLayers.len(), + LayerType::Background); + } + for (layer, other) in self.gecko.mImage.mLayers.iter_mut() + .zip(other.gecko.mImage.mLayers.iter()) { + layer.mPosition.m${orientation[0].upper()}Position + = other.mPosition.m${orientation[0].upper()}Position; + } + self.gecko.mImage.mPosition${orientation[0].upper()}Count + = other.gecko.mImage.mPosition${orientation[0].upper()}Count; + } + + pub fn clone_background_position_${orientation[0]}(&self) + -> longhands::background_position_${orientation[0]}::computed_value::T { + use values::computed::position::${orientation[1]}Position; + longhands::background_position_${orientation[0]}::computed_value::T( + self.gecko.mImage.mLayers.iter() + .take(self.gecko.mImage.mPosition${orientation[0].upper()}Count as usize) + .map(|position| ${orientation[1]}Position(position.mPosition.m${orientation[0].upper()}Position.into())) + .collect() + ) + } + + pub fn set_background_position_${orientation[0]}(&mut self, + v: longhands::background_position_${orientation[0]}::computed_value::T) { + use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; + + unsafe { + Gecko_EnsureImageLayersLength(&mut self.gecko.mImage, v.0.len(), + LayerType::Background); + } + + self.gecko.mImage.mPosition${orientation[0].upper()}Count = v.0.len() as u32; + for (servo, geckolayer) in v.0.into_iter().zip(self.gecko.mImage + .mLayers.iter_mut()) { + geckolayer.mPosition.m${orientation[0].upper()}Position = servo.0.into(); + } + } + % endfor </%self:impl_trait> <%self:impl_trait style_struct_name="List" diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 1053e8e6958..1e813ba78e1 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -7,7 +7,8 @@ use cssparser::{Color as CSSParserColor, Parser, RGBA}; use euclid::{Point2D, Size2D}; use properties::PropertyDeclaration; use properties::longhands; -use properties::longhands::background_position::computed_value::T as BackgroundPosition; +use properties::longhands::background_position_x::computed_value::T as BackgroundPositionX; +use properties::longhands::background_position_y::computed_value::T as BackgroundPositionY; use properties::longhands::background_size::computed_value::T as BackgroundSize; use properties::longhands::font_weight::computed_value::T as FontWeight; use properties::longhands::line_height::computed_value::T as LineHeight; @@ -26,7 +27,7 @@ use values::Either; use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; use values::computed::{BorderRadiusSize, LengthOrNone}; use values::computed::{CalcLengthOrPercentage, LengthOrPercentage}; -use values::computed::position::Position; +use values::computed::position::{HorizontalPosition, Position, VerticalPosition}; use values::computed::ToComputedValue; @@ -574,10 +575,37 @@ impl Interpolate for Position { impl RepeatableListInterpolate for Position {} -impl Interpolate for BackgroundPosition { +/// https://drafts.csswg.org/css-transitions/#animtype-simple-list +impl Interpolate for HorizontalPosition { + #[inline] + fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + Ok(HorizontalPosition(try!(self.0.interpolate(&other.0, progress)))) + } +} + +impl RepeatableListInterpolate for HorizontalPosition {} + +/// https://drafts.csswg.org/css-transitions/#animtype-simple-list +impl Interpolate for VerticalPosition { + #[inline] + fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + Ok(VerticalPosition(try!(self.0.interpolate(&other.0, progress)))) + } +} + +impl RepeatableListInterpolate for VerticalPosition {} + +impl Interpolate for BackgroundPositionX { + #[inline] + fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + Ok(BackgroundPositionX(try!(self.0.interpolate(&other.0, progress)))) + } +} + +impl Interpolate for BackgroundPositionY { #[inline] fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { - Ok(BackgroundPosition(try!(self.0.interpolate(&other.0, progress)))) + Ok(BackgroundPositionY(try!(self.0.interpolate(&other.0, progress)))) } } diff --git a/components/style/properties/longhand/background.mako.rs b/components/style/properties/longhand/background.mako.rs index adc7e44e0a9..645e9666c63 100644 --- a/components/style/properties/longhand/background.mako.rs +++ b/components/style/properties/longhand/background.mako.rs @@ -86,43 +86,89 @@ ${helpers.predefined_type("background-color", "CSSColor", } </%helpers:vector_longhand> -<%helpers:vector_longhand name="background-position" animatable="True"> +<%helpers:vector_longhand name="background-position-x" animatable="True"> use std::fmt; use style_traits::ToCss; use values::HasViewportPercentage; - use values::specified::position::Position; + use values::specified::position::HorizontalPosition; pub mod computed_value { - use values::computed::position::Position; + use values::computed::position::HorizontalPosition; use properties::animated_properties::{Interpolate, RepeatableListInterpolate}; - pub type T = Position; + pub type T = HorizontalPosition; } - pub type SpecifiedValue = Position; + pub type SpecifiedValue = HorizontalPosition; #[inline] pub fn get_initial_value() -> computed_value::T { - use values::computed::position::Position; - Position { - horizontal: computed::LengthOrPercentage::Percentage(0.0), - vertical: computed::LengthOrPercentage::Percentage(0.0), + use values::computed::position::HorizontalPosition; + HorizontalPosition(computed::LengthOrPercentage::Percentage(0.0)) + } + #[inline] + pub fn get_initial_specified_value() -> SpecifiedValue { + use values::specified::position::Keyword; + HorizontalPosition { + keyword: Some(Keyword::Left), + position: None, } } #[inline] + pub fn get_initial_position_value() -> SpecifiedValue { + use values::specified::{LengthOrPercentage, Percentage}; + HorizontalPosition { + keyword: None, + position: Some(LengthOrPercentage::Percentage(Percentage(0.0))), + } + } + + pub fn parse(context: &ParserContext, input: &mut Parser) + -> Result<SpecifiedValue, ()> { + HorizontalPosition::parse(context, input) + } +</%helpers:vector_longhand> + +<%helpers:vector_longhand name="background-position-y" animatable="True"> + use std::fmt; + use style_traits::ToCss; + use values::HasViewportPercentage; + use values::specified::position::VerticalPosition; + + pub mod computed_value { + use values::computed::position::VerticalPosition; + use properties::animated_properties::{Interpolate, RepeatableListInterpolate}; + + pub type T = VerticalPosition; + } + + pub type SpecifiedValue = VerticalPosition; + + #[inline] + pub fn get_initial_value() -> computed_value::T { + use values::computed::position::VerticalPosition; + VerticalPosition(computed::LengthOrPercentage::Percentage(0.0)) + } + #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { - use values::specified::Percentage; - 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))), + use values::specified::position::Keyword; + VerticalPosition { + keyword: Some(Keyword::Top), + position: None, + } + } + #[inline] + pub fn get_initial_position_value() -> SpecifiedValue { + use values::specified::{LengthOrPercentage, Percentage}; + VerticalPosition { + keyword: None, + position: Some(LengthOrPercentage::Percentage(Percentage(0.0))), } } pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { - Ok(try!(Position::parse(context, input))) + VerticalPosition::parse(context, input) } </%helpers:vector_longhand> diff --git a/components/style/properties/longhand/svg.mako.rs b/components/style/properties/longhand/svg.mako.rs index a0d107f6326..dcab70f6b34 100644 --- a/components/style/properties/longhand/svg.mako.rs +++ b/components/style/properties/longhand/svg.mako.rs @@ -91,21 +91,59 @@ ${helpers.single_keyword("mask-repeat", products="gecko", animatable=False)} -<%helpers:longhand name="mask-position" products="gecko" animatable="True"> - use properties::longhands::background_position; - pub use ::properties::longhands::background_position::SpecifiedValue; - pub use ::properties::longhands::background_position::single_value as single_value; - pub use ::properties::longhands::background_position::computed_value as computed_value; +<%helpers:vector_longhand name="mask-position" products="gecko" animatable="True"> + use std::fmt; + use style_traits::ToCss; + use values::HasViewportPercentage; + use values::specified::position::Position; + + pub mod computed_value { + use values::computed::position::Position; + use properties::animated_properties::{Interpolate, RepeatableListInterpolate}; + use properties::longhands::mask_position::computed_value::T as MaskPosition; + + pub type T = Position; + + impl RepeatableListInterpolate for MaskPosition {} + + impl Interpolate for MaskPosition { + fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + Ok(MaskPosition(try!(self.0.interpolate(&other.0, progress)))) + } + } + } + + pub type SpecifiedValue = Position; #[inline] pub fn get_initial_value() -> computed_value::T { - background_position::get_initial_value() + use values::computed::position::Position; + Position { + horizontal: computed::LengthOrPercentage::Percentage(0.0), + vertical: computed::LengthOrPercentage::Percentage(0.0), + } + } + #[inline] + pub fn get_initial_specified_value() -> SpecifiedValue { + use values::specified::Percentage; + use values::specified::position::{HorizontalPosition, VerticalPosition}; + Position { + horizontal: HorizontalPosition { + keyword: None, + position: Some(specified::LengthOrPercentage::Percentage(Percentage(0.0))), + }, + vertical: VerticalPosition { + keyword: None, + position: Some(specified::LengthOrPercentage::Percentage(Percentage(0.0))), + }, + } } - pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { - background_position::parse(context, input) + pub fn parse(context: &ParserContext, input: &mut Parser) + -> Result<SpecifiedValue, ()> { + Position::parse(context, input) } -</%helpers:longhand> +</%helpers:vector_longhand> // missing: margin-box fill-box stroke-box view-box no-clip // (gecko doesn't implement these) diff --git a/components/style/properties/shorthand/background.mako.rs b/components/style/properties/shorthand/background.mako.rs index 2537a71c29f..d6ba7da83b8 100644 --- a/components/style/properties/shorthand/background.mako.rs +++ b/components/style/properties/shorthand/background.mako.rs @@ -6,10 +6,14 @@ // TODO: other background-* properties <%helpers:shorthand name="background" - sub_properties="background-color background-position background-repeat background-attachment - background-image background-size background-origin background-clip"> - use properties::longhands::{background_color, background_position, background_repeat, background_attachment}; - use properties::longhands::{background_image, background_size, background_origin, background_clip}; + sub_properties="background-color background-position-x background-position-y background-repeat + background-attachment background-image background-size background-origin + background-clip"> + use properties::longhands::{background_color, background_position_x, background_position_y, background_repeat}; + use properties::longhands::{background_attachment, background_image, background_size, background_origin}; + use properties::longhands::background_clip; + use values::specified::position::Position; + use parser::Parse; impl From<background_origin::single_value::SpecifiedValue> for background_clip::single_value::SpecifiedValue { fn from(origin: background_origin::single_value::SpecifiedValue) -> @@ -28,11 +32,11 @@ pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> { let mut background_color = None; - % for name in "image position repeat size attachment origin clip".split(): + % for name in "image position_x position_y repeat size attachment origin clip".split(): let mut background_${name} = background_${name}::SpecifiedValue(Vec::new()); % endfor try!(input.parse_comma_separated(|input| { - % for name in "image position repeat size attachment origin clip".split(): + % for name in "image position_x position_y repeat size attachment origin clip".split(): let mut ${name} = None; % endfor loop { @@ -45,10 +49,10 @@ return Err(()) } } - if position.is_none() { - if let Ok(value) = input.try(|input| background_position::single_value - ::parse(context, input)) { - position = Some(value); + if position_x.is_none() && position_y.is_none() { + if let Ok(value) = input.try(|input| Position::parse(context, input)) { + position_x = Some(value.horizontal); + position_y = Some(value.vertical); // Parse background size, if applicable. size = input.try(|input| { @@ -76,12 +80,24 @@ } } let mut any = false; - % for name in "image position repeat size attachment origin clip".split(): + % for name in "image position_x position_y repeat size attachment origin clip".split(): any = any || ${name}.is_some(); % endfor any = any || background_color.is_some(); if any { - % for name in "image position repeat size attachment origin clip".split(): + if position_x.is_some() || position_y.is_some() { + % for name in "position_x position_y".split(): + if let Some(bg_${name}) = ${name} { + background_${name}.0.push(bg_${name}); + } + % endfor + } else { + % for name in "position_x position_y".split(): + background_${name}.0.push(background_${name}::single_value + ::get_initial_position_value()); + % endfor + } + % for name in "image repeat size attachment origin clip".split(): if let Some(bg_${name}) = ${name} { background_${name}.0.push(bg_${name}); } else { @@ -98,7 +114,8 @@ Ok(Longhands { background_color: background_color, background_image: Some(background_image), - background_position: Some(background_position), + background_position_x: Some(background_position_x), + background_position_y: Some(background_position_y), background_repeat: Some(background_repeat), background_attachment: Some(background_attachment), background_size: Some(background_size), @@ -118,7 +135,7 @@ } use std::cmp; let mut len = 0; - % for name in "image position repeat size attachment origin clip".split(): + % for name in "image position_x position_y repeat size attachment origin clip".split(): len = cmp::max(len, extract_value(self.background_${name}).map(|i| i.0.len()) .unwrap_or(0)); % endfor @@ -130,7 +147,7 @@ let mut first = true; for i in 0..len { - % for name in "image position repeat size attachment origin clip".split(): + % for name in "image position_x position_y repeat size attachment origin clip".split(): let ${name} = if let DeclaredValue::Value(ref arr) = *self.background_${name} { arr.0.get(i % arr.0.len()) } else { @@ -185,8 +202,14 @@ try!(write!(dest, " ")); - try!(position.unwrap_or(&background_position::single_value - ::get_initial_specified_value()) + try!(position_x.unwrap_or(&background_position_x::single_value + ::get_initial_position_value()) + .to_css(dest)); + + try!(write!(dest, " ")); + + try!(position_y.unwrap_or(&background_position_y::single_value + ::get_initial_position_value()) .to_css(dest)); if let Some(size) = size { @@ -227,3 +250,84 @@ } } </%helpers:shorthand> + +<%helpers:shorthand name="background-position" + sub_properties="background-position-x background-position-y"> + use properties::longhands::{background_position_x,background_position_y}; + use values::specified::position::Position; + use parser::Parse; + + pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> { + let mut position_x = background_position_x::SpecifiedValue(Vec::new()); + let mut position_y = background_position_y::SpecifiedValue(Vec::new()); + let mut any = false; + + try!(input.parse_comma_separated(|input| { + loop { + if let Ok(value) = input.try(|input| Position::parse(context, input)) { + position_x.0.push(value.horizontal); + position_y.0.push(value.vertical); + any = true; + continue + } + break + } + Ok(()) + })); + if any == false { + return Err(()); + } + + Ok(Longhands { + background_position_x: Some(position_x), + background_position_y: Some(position_y), + }) + } + + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + // mako doesn't like ampersands following `<` + fn extract_value<T>(x: &DeclaredValue<T>) -> Option< &T> { + match *x { + DeclaredValue::Value(ref val) => Some(val), + _ => None, + } + } + use std::cmp; + let mut len = 0; + % for name in "x y".split(): + len = cmp::max(len, extract_value(self.background_position_${name}) + .map(|i| i.0.len()) + .unwrap_or(0)); + % endfor + + // There should be at least one declared value + if len == 0 { + return dest.write_str("") + } + + for i in 0..len { + % for name in "x y".split(): + let position_${name} = if let DeclaredValue::Value(ref arr) = + *self.background_position_${name} { + arr.0.get(i % arr.0.len()) + } else { + None + }; + % endfor + + try!(position_x.unwrap_or(&background_position_x::single_value + ::get_initial_position_value()) + .to_css(dest)); + + try!(write!(dest, " ")); + + try!(position_y.unwrap_or(&background_position_y::single_value + ::get_initial_position_value()) + .to_css(dest)); + } + + Ok(()) + } + } +</%helpers:shorthand> diff --git a/components/style/servo/restyle_damage.rs b/components/style/servo/restyle_damage.rs index f7635d80a64..fb17071321a 100644 --- a/components/style/servo/restyle_damage.rs +++ b/components/style/servo/restyle_damage.rs @@ -232,10 +232,10 @@ fn compute_damage(old: &ServoComputedValues, new: &ServoComputedValues) -> Servo ]) || add_if_not_equal!(old, new, damage, [REPAINT], [ get_color.color, get_background.background_color, - get_background.background_image, get_background.background_position, - get_background.background_repeat, get_background.background_attachment, - get_background.background_clip, get_background.background_origin, - get_background.background_size, + get_background.background_image, get_background.background_position_x, + get_background.background_position_y, get_background.background_repeat, + get_background.background_attachment, get_background.background_clip, + get_background.background_origin, get_background.background_size, get_border.border_top_color, get_border.border_right_color, get_border.border_bottom_color, get_border.border_left_color, get_border.border_top_style, get_border.border_right_style, diff --git a/components/style/values/computed/position.rs b/components/style/values/computed/position.rs index d2a561639f4..f93d59db99c 100644 --- a/components/style/values/computed/position.rs +++ b/components/style/values/computed/position.rs @@ -26,3 +26,23 @@ impl ToCss for Position { Ok(()) } } + +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct HorizontalPosition(pub LengthOrPercentage); + +impl ToCss for HorizontalPosition { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.0.to_css(dest) + } +} + +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct VerticalPosition(pub LengthOrPercentage); + +impl ToCss for VerticalPosition { + fn to_css<W>(&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<W>(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<W>(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<Keyword>, - pub horiz_position: Option<LengthOrPercentage>, - pub vert_keyword: Option<Keyword>, - pub vert_position: Option<LengthOrPercentage>, + pub horizontal: HorizontalPosition, + pub vertical: VerticalPosition, } impl ToCss for Position { fn to_css<W>(&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<PositionComponent>, mut second_position: Option<PositionComponent>, first_keyword: Option<PositionComponent>, second_keyword: Option<PositionComponent>) @@ -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,66 +214,126 @@ impl Parse for Position { } } -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)), +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, } } -} -impl ToCss for Keyword { - fn to_css<W>(&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")), + #[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)), + }, } - Ok(()) } } -// Collapse `Position` into a few categories to simplify the above `match` expression. -enum PositionCategory { - HorizontalKeyword, - VerticalKeyword, - OtherKeyword, - LengthOrPercentage, +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct HorizontalPosition { + pub keyword: Option<Keyword>, + pub position: Option<LengthOrPercentage>, } -fn category(p: PositionComponent) -> PositionCategory { - if let PositionComponent::Keyword(keyword) = p { - match keyword { - Keyword::Left | - Keyword::Right => - PositionCategory::HorizontalKeyword, - Keyword::Top | - Keyword::Bottom => - PositionCategory::VerticalKeyword, - Keyword::Center => - PositionCategory::OtherKeyword, +impl HasViewportPercentage for HorizontalPosition { + fn has_viewport_percentage(&self) -> bool { + if let Some(pos) = self.position { + pos.has_viewport_percentage() + } else { + false } - } else { - PositionCategory::LengthOrPercentage } } -impl ToComputedValue for Position { - type ComputedValue = computed_position::Position; +impl ToCss for HorizontalPosition { + fn to_css<W>(&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 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); + fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { + 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 <length>" 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 horiz_keyword { - Keyword::Right => { - if let Some(x) = self.horiz_position { + 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)), @@ -304,18 +348,121 @@ impl ToComputedValue for Position { } }, Keyword::Center => { - horiz_keyword.to_length_or_percentage().to_computed_value(context) + keyword.to_length_or_percentage().to_computed_value(context) }, _ => { - let horiz = self.horiz_position.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.0))); + 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<Keyword>, + pub position: Option<LengthOrPercentage>, +} + +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<W>(&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<Self, ()> { + 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 <length>" 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 vert_keyword { - Keyword::Bottom => { - if let Some(x) = self.vert_position { + 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)), @@ -330,31 +477,87 @@ impl ToComputedValue for Position { } }, Keyword::Center => { - vert_keyword.to_length_or_percentage().to_computed_value(context) + keyword.to_length_or_percentage().to_computed_value(context) }, _ => { - let vert = self.vert_position.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.0))); + let vert = self.position + .unwrap_or(LengthOrPercentage::Percentage(Percentage(0.0))); vert.to_computed_value(context) }, }; - computed_position::Position { - horizontal: horizontal, - vertical: vertical, - } + computed_position::VerticalPosition(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)), + 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)), } } } +// 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 => + PositionCategory::HorizontalKeyword, + Keyword::Top | Keyword::Bottom => + PositionCategory::VerticalKeyword, + Keyword::XStart | Keyword::XEnd => + PositionCategory::HorizontalLogicalKeyword, + Keyword::YStart | Keyword::YEnd => + PositionCategory::VerticalLogicalKeyword, + Keyword::Center => + PositionCategory::OtherKeyword, + } + } else { + PositionCategory::LengthOrPercentage + } +} + 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/background.rs b/tests/unit/style/parsing/background.rs index 278291d70e7..f5a6ceda850 100644 --- a/tests/unit/style/parsing/background.rs +++ b/tests/unit/style/parsing/background.rs @@ -7,7 +7,8 @@ use media_queries::CSSErrorReporterTest; use servo_url::ServoUrl; use style::parser::ParserContext; use style::properties::longhands::{background_attachment, background_clip, background_color, background_image}; -use style::properties::longhands::{background_origin, background_position, background_repeat, background_size}; +use style::properties::longhands::{background_origin, background_position_x, background_position_y, background_repeat}; +use style::properties::longhands::background_size; use style::properties::shorthands::background; use style::stylesheets::Origin; @@ -20,7 +21,8 @@ fn background_shorthand_should_parse_all_available_properties_when_specified() { let result = background::parse_value(&context, &mut parser).unwrap(); assert_eq!(result.background_image.unwrap(), parse_longhand!(background_image, "url(\"http://servo/test.png\")")); - assert_eq!(result.background_position.unwrap(), parse_longhand!(background_position, "top center")); + assert_eq!(result.background_position_x.unwrap(), parse_longhand!(background_position_x, "center")); + assert_eq!(result.background_position_y.unwrap(), parse_longhand!(background_position_y, "top")); assert_eq!(result.background_size.unwrap(), parse_longhand!(background_size, "200px 200px")); assert_eq!(result.background_repeat.unwrap(), parse_longhand!(background_repeat, "repeat-x")); assert_eq!(result.background_attachment.unwrap(), parse_longhand!(background_attachment, "fixed")); @@ -36,7 +38,8 @@ fn background_shorthand_should_parse_when_some_fields_set() { let mut parser = Parser::new("14px 40px repeat-y"); let result = background::parse_value(&context, &mut parser).unwrap(); - assert_eq!(result.background_position.unwrap(), parse_longhand!(background_position, "14px 40px")); + assert_eq!(result.background_position_x.unwrap(), parse_longhand!(background_position_x, "14px")); + assert_eq!(result.background_position_y.unwrap(), parse_longhand!(background_position_y, "40px")); assert_eq!(result.background_repeat.unwrap(), parse_longhand!(background_repeat, "repeat-y")); let mut parser = Parser::new("url(\"http://servo/test.png\") repeat blue"); @@ -68,8 +71,8 @@ fn background_shorthand_should_parse_comma_separated_declarations() { assert_eq!(result.background_image.unwrap(), parse_longhand!(background_image, "url(\"http://servo/test.png\"), \ url(\"http://servo/test.png\"), none")); - assert_eq!(result.background_position.unwrap(), parse_longhand!(background_position, "left top, center center, \ - 0% 0%")); + assert_eq!(result.background_position_x.unwrap(), parse_longhand!(background_position_x, "left, center, 0%")); + assert_eq!(result.background_position_y.unwrap(), parse_longhand!(background_position_y, "top, center, 0%")); assert_eq!(result.background_repeat.unwrap(), parse_longhand!(background_repeat, "no-repeat, no-repeat, repeat")); assert_eq!(result.background_clip.unwrap(), parse_longhand!(background_clip, "border-box, border-box, border-box")); assert_eq!(result.background_origin.unwrap(), parse_longhand!(background_origin, "padding-box, padding-box, \ @@ -86,12 +89,14 @@ fn background_shorthand_should_parse_position_and_size_correctly() { let mut parser = Parser::new("7px 4px"); let result = background::parse_value(&context, &mut parser).unwrap(); - assert_eq!(result.background_position.unwrap(), parse_longhand!(background_position, "7px 4px")); + assert_eq!(result.background_position_x.unwrap(), parse_longhand!(background_position_x, "7px")); + assert_eq!(result.background_position_y.unwrap(), parse_longhand!(background_position_y, "4px")); let mut parser = Parser::new("7px 4px / 30px 20px"); let result = background::parse_value(&context, &mut parser).unwrap(); - assert_eq!(result.background_position.unwrap(), parse_longhand!(background_position, "7px 4px")); + assert_eq!(result.background_position_x.unwrap(), parse_longhand!(background_position_x, "7px")); + assert_eq!(result.background_position_y.unwrap(), parse_longhand!(background_position_y, "4px")); assert_eq!(result.background_size.unwrap(), parse_longhand!(background_size, "30px 20px")); let mut parser = Parser::new("/ 30px 20px"); 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..21e490a86c1 100644 --- a/tests/unit/style/properties/serialization.rs +++ b/tests/unit/style/properties/serialization.rs @@ -688,11 +688,12 @@ mod shorthand_serialization { use style::properties::longhands::background_clip as clip; use style::properties::longhands::background_image as image; use style::properties::longhands::background_origin as origin; - use style::properties::longhands::background_position as position; + use style::properties::longhands::background_position_x as position_x; + use style::properties::longhands::background_position_y as position_y; 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, VerticalPosition}; use super::*; macro_rules! single_vec_value_typedef { ($name:ident, $path:expr) => { @@ -731,12 +732,17 @@ mod shorthand_serialization { authored: None }); - 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))) + let position_x = single_vec_value_typedef!(position_x, + HorizontalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(7f32))), + } + ); + + let position_y = single_vec_value_typedef!(position_y, + VerticalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(4f32))), } ); @@ -759,7 +765,8 @@ mod shorthand_serialization { let clip = single_vec_keyword_value!(clip, padding_box); properties.push(PropertyDeclaration::BackgroundColor(color)); - properties.push(PropertyDeclaration::BackgroundPosition(position)); + properties.push(PropertyDeclaration::BackgroundPositionX(position_x)); + properties.push(PropertyDeclaration::BackgroundPositionY(position_y)); properties.push(PropertyDeclaration::BackgroundRepeat(repeat)); properties.push(PropertyDeclaration::BackgroundAttachment(attachment)); properties.push(PropertyDeclaration::BackgroundImage(image)); @@ -785,12 +792,17 @@ mod shorthand_serialization { authored: None }); - 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))) + let position_x = single_vec_value_typedef!(position_x, + HorizontalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(7f32))), + } + ); + + let position_y = single_vec_value_typedef!(position_y, + VerticalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(4f32))), } ); @@ -813,7 +825,8 @@ mod shorthand_serialization { let clip = single_vec_keyword_value!(clip, padding_box); properties.push(PropertyDeclaration::BackgroundColor(color)); - properties.push(PropertyDeclaration::BackgroundPosition(position)); + properties.push(PropertyDeclaration::BackgroundPositionX(position_x)); + properties.push(PropertyDeclaration::BackgroundPositionY(position_y)); properties.push(PropertyDeclaration::BackgroundRepeat(repeat)); properties.push(PropertyDeclaration::BackgroundAttachment(attachment)); properties.push(PropertyDeclaration::BackgroundImage(image)); @@ -838,12 +851,17 @@ mod shorthand_serialization { authored: None }); - 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))) + let position_x = single_vec_value_typedef!(position_x, + HorizontalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(0f32))), + } + ); + + let position_y = single_vec_value_typedef!(position_y, + VerticalPosition { + keyword: None, + position: Some(LengthOrPercentage::Length(Length::from_px(0f32))), } ); @@ -858,7 +876,8 @@ mod shorthand_serialization { let clip = DeclaredValue::Initial; properties.push(PropertyDeclaration::BackgroundColor(color)); - properties.push(PropertyDeclaration::BackgroundPosition(position)); + properties.push(PropertyDeclaration::BackgroundPositionX(position_x)); + properties.push(PropertyDeclaration::BackgroundPositionY(position_y)); properties.push(PropertyDeclaration::BackgroundRepeat(repeat)); properties.push(PropertyDeclaration::BackgroundAttachment(attachment)); properties.push(PropertyDeclaration::BackgroundImage(image)); @@ -881,7 +900,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 +937,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 +991,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))), + }, } ); diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs index a5a5be5a7d1..d4c8bcc28a1 100644 --- a/tests/unit/style/stylesheets.rs +++ b/tests/unit/style/stylesheets.rs @@ -190,10 +190,15 @@ fn test_parse_stylesheet() { } )), Importance::Normal), - (PropertyDeclaration::BackgroundPosition(DeclaredValue::Value( - longhands::background_position::SpecifiedValue( - vec![longhands::background_position::single_value - ::get_initial_specified_value()]))), + (PropertyDeclaration::BackgroundPositionX(DeclaredValue::Value( + longhands::background_position_x::SpecifiedValue( + vec![longhands::background_position_x::single_value + ::get_initial_position_value()]))), + Importance::Normal), + (PropertyDeclaration::BackgroundPositionY(DeclaredValue::Value( + longhands::background_position_y::SpecifiedValue( + vec![longhands::background_position_y::single_value + ::get_initial_position_value()]))), Importance::Normal), (PropertyDeclaration::BackgroundRepeat(DeclaredValue::Value( longhands::background_repeat::SpecifiedValue( diff --git a/tests/wpt/metadata-css/css-backgrounds-3_dev/html4/background-331.htm.ini b/tests/wpt/metadata-css/css-backgrounds-3_dev/html4/background-331.htm.ini index aeae4c583a2..8d83039b0a6 100644 --- a/tests/wpt/metadata-css/css-backgrounds-3_dev/html4/background-331.htm.ini +++ b/tests/wpt/metadata-css/css-backgrounds-3_dev/html4/background-331.htm.ini @@ -6,3 +6,6 @@ [background_initial_color] expected: FAIL + [background_initial_position] + expected: FAIL + diff --git a/tests/wpt/metadata-css/css-backgrounds-3_dev/html4/background-333.htm.ini b/tests/wpt/metadata-css/css-backgrounds-3_dev/html4/background-333.htm.ini index 04fd52fd845..11c7900a2d9 100644 --- a/tests/wpt/metadata-css/css-backgrounds-3_dev/html4/background-333.htm.ini +++ b/tests/wpt/metadata-css/css-backgrounds-3_dev/html4/background-333.htm.ini @@ -6,3 +6,6 @@ [background_specified_color_color] expected: FAIL + [background_specified_color_position] + expected: FAIL + |