aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2016-12-15 12:15:06 -0800
committerGitHub <noreply@github.com>2016-12-15 12:15:06 -0800
commit5357f05ff785ee160d6b07de5d0e10aba199e578 (patch)
tree1fce6fc1ede31c0b58cfcee318c0872450e9e0c6
parentb1337da1143dfe4d494c39cdce78e1469c200ff5 (diff)
parentca85221525962dce862a67600654d41fd744cf36 (diff)
downloadservo-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 -->
-rw-r--r--components/layout/display_list_builder.rs7
-rw-r--r--components/script/dom/webidls/CSSStyleDeclaration.webidl4
-rw-r--r--components/style/properties/gecko.mako.rs58
-rw-r--r--components/style/properties/helpers/animated_properties.mako.rs36
-rw-r--r--components/style/properties/longhand/background.mako.rs78
-rw-r--r--components/style/properties/longhand/svg.mako.rs56
-rw-r--r--components/style/properties/shorthand/background.mako.rs138
-rw-r--r--components/style/servo/restyle_damage.rs8
-rw-r--r--components/style/values/computed/position.rs20
-rw-r--r--components/style/values/specified/basic_shape.rs31
-rw-r--r--components/style/values/specified/position.rs427
-rw-r--r--tests/unit/style/parsing/background.rs19
-rw-r--r--tests/unit/style/parsing/position.rs75
-rw-r--r--tests/unit/style/properties/serialization.rs91
-rw-r--r--tests/unit/style/stylesheets.rs13
-rw-r--r--tests/wpt/metadata-css/css-backgrounds-3_dev/html4/background-331.htm.ini3
-rw-r--r--tests/wpt/metadata-css/css-backgrounds-3_dev/html4/background-333.htm.ini3
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
+