From 70ec61cf01907c064c53563b46ea361b6a0348fe Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Mon, 8 May 2017 03:09:26 +0200 Subject: [PATCH] Refactor Position A specified position is now a struct made of two values of different types, the first one being PositionComponent, and the second one PositionComponent. A position component is represented by the new enum PositionComponent, with the three values Center, Length(LengthOrPercentage), and Side(Side, Option). Side keywords are represented by the X and Y enums, which don't include a value for the center keyword anymore. They are accompanied by the Side trait, which allows us to determine whether a side keyword is "left" or "top". This refactor simplified the parsing and serialisation code and exposed bugs in it, where it would reject valid values followed by arbitrary tokens, and where it would fail to prefer "left" to "right" when serialising positions in basic shapes. --- components/layout/display_list_builder.rs | 8 +- components/style/gecko/conversions.rs | 13 +- components/style/properties/gecko.mako.rs | 44 +- .../helpers/animated_properties.mako.rs | 42 +- .../properties/longhand/background.mako.rs | 39 +- .../style/properties/longhand/box.mako.rs | 6 +- .../style/properties/longhand/svg.mako.rs | 51 +- .../properties/shorthand/background.mako.rs | 45 +- .../style/properties/shorthand/mask.mako.rs | 43 +- components/style/values/computed/position.rs | 65 +-- components/style/values/generics/position.rs | 273 +--------- .../style/values/specified/basic_shape.rs | 118 ++--- components/style/values/specified/mod.rs | 2 +- components/style/values/specified/position.rs | 491 +++++++++--------- tests/unit/style/parsing/basic_shape.rs | 2 +- tests/unit/style/parsing/mod.rs | 4 + tests/unit/style/parsing/position.rs | 80 +-- tests/unit/style/parsing/value.rs | 1 - tests/unit/style/properties/serialization.rs | 30 +- tests/unit/style/stylesheets.rs | 10 +- tests/wpt/mozilla/meta/MANIFEST.json | 2 +- tests/wpt/mozilla/tests/mozilla/calc.html | 2 +- 22 files changed, 484 insertions(+), 887 deletions(-) diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index b48bb607ac7..472a34529ca 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -1008,9 +1008,9 @@ impl FragmentDisplayListBuilding for Fragment { 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(horiz_position.0, + let horizontal_position = model::specified(horiz_position, bounds.size.width - image_size.width); - let vertical_position = model::specified(vert_position.0, + let vertical_position = model::specified(vert_position, bounds.size.height - image_size.height); // The anchor position for this background, based on both the background-attachment @@ -1167,8 +1167,8 @@ impl FragmentDisplayListBuilding for Fragment { repeating: bool, style: &ServoComputedValues) -> display_list::RadialGradient { - let center = Point2D::new(specified(center.horizontal.0, bounds.size.width), - specified(center.vertical.0, bounds.size.height)); + let center = Point2D::new(specified(center.horizontal, bounds.size.width), + specified(center.vertical, bounds.size.height)); let radius = match *shape { EndingShape::Circle(LengthOrKeyword::Length(length)) => Size2D::new(length, length), diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 5b2e726583e..bb299a74db1 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -307,8 +307,8 @@ impl nsStyleImage { }, } unsafe { - (*gecko_gradient).mBgPosX.set(position.horizontal.0); - (*gecko_gradient).mBgPosY.set(position.vertical.0); + (*gecko_gradient).mBgPosX.set(position.horizontal); + (*gecko_gradient).mBgPosY.set(position.vertical); } gecko_gradient @@ -372,7 +372,6 @@ pub mod basic_shape { use values::computed::position; use values::generics::BorderRadiusSize as GenericBorderRadiusSize; use values::generics::basic_shape::FillRule; - use values::generics::position::{HorizontalPosition, VerticalPosition}; // using Borrow so that we can have a non-moving .into() impl> From for BasicShape { @@ -483,8 +482,8 @@ pub mod basic_shape { impl From for structs::Position { fn from(other: position::Position) -> Self { structs::Position { - mXPosition: other.horizontal.0.into(), - mYPosition: other.vertical.0.into() + mXPosition: other.horizontal.into(), + mYPosition: other.vertical.into() } } } @@ -501,8 +500,8 @@ pub mod basic_shape { fn from(other: T) -> Self { let other = other.borrow(); position::Position { - horizontal: HorizontalPosition(other.mXPosition.into()), - vertical: VerticalPosition(other.mYPosition.into()), + horizontal: other.mXPosition.into(), + vertical: other.mYPosition.into(), } } } diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 4995e9abfce..0df847cfd95 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -367,17 +367,16 @@ fn color_to_nscolor_zero_currentcolor(color: Color) -> structs::nscolor { <%def name="impl_position(ident, gecko_ffi_name, need_clone=False)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { - ${set_gecko_property("%s.mXPosition" % gecko_ffi_name, "v.horizontal.0.into()")} - ${set_gecko_property("%s.mYPosition" % gecko_ffi_name, "v.vertical.0.into()")} + ${set_gecko_property("%s.mXPosition" % gecko_ffi_name, "v.horizontal.into()")} + ${set_gecko_property("%s.mYPosition" % gecko_ffi_name, "v.vertical.into()")} } <%call expr="impl_simple_copy(ident, gecko_ffi_name)"> % if need_clone: #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { - use values::generics::position::{HorizontalPosition, Position, VerticalPosition}; - Position { - horizontal: HorizontalPosition(self.gecko.${gecko_ffi_name}.mXPosition.into()), - vertical: VerticalPosition(self.gecko.${gecko_ffi_name}.mYPosition.into()), + longhands::${ident}::computed_value::T { + horizontal: self.gecko.${gecko_ffi_name}.mXPosition.into(), + vertical: self.gecko.${gecko_ffi_name}.mYPosition.into(), } } % endif @@ -2040,8 +2039,8 @@ fn static_assert() { for (gecko, servo) in self.gecko.mScrollSnapCoordinate .iter_mut() .zip(v) { - gecko.mXPosition = servo.horizontal.0.into(); - gecko.mYPosition = servo.vertical.0.into(); + gecko.mXPosition = servo.horizontal.into(); + gecko.mYPosition = servo.vertical.into(); } } @@ -2726,12 +2725,12 @@ fn static_assert() { } - % for orientation in [("x", "Horizontal"), ("y", "Vertical")]: - pub fn copy_${shorthand}_position_${orientation[0]}_from(&mut self, other: &Self) { + % for orientation in ["x", "y"]: + pub fn copy_${shorthand}_position_${orientation}_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; - self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count - = cmp::min(1, other.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count); + self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count + = cmp::min(1, other.gecko.${image_layers_field}.mPosition${orientation.upper()}Count); self.gecko.${image_layers_field}.mLayers.mFirstElement.mPosition = other.gecko.${image_layers_field}.mLayers.mFirstElement.mPosition; unsafe { @@ -2742,20 +2741,19 @@ fn static_assert() { for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut() .zip(other.gecko.${image_layers_field}.mLayers.iter()) { - layer.mPosition.m${orientation[0].upper()}Position - = other.mPosition.m${orientation[0].upper()}Position; + layer.mPosition.m${orientation.upper()}Position + = other.mPosition.m${orientation.upper()}Position; } - self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count - = other.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count; + self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count + = other.gecko.${image_layers_field}.mPosition${orientation.upper()}Count; } - pub fn clone_${shorthand}_position_${orientation[0]}(&self) - -> longhands::${shorthand}_position_${orientation[0]}::computed_value::T { - use values::generics::position::${orientation[1]}Position; - longhands::${shorthand}_position_${orientation[0]}::computed_value::T( + pub fn clone_${shorthand}_position_${orientation}(&self) + -> longhands::${shorthand}_position_${orientation}::computed_value::T { + longhands::${shorthand}_position_${orientation}::computed_value::T( self.gecko.${image_layers_field}.mLayers.iter() - .take(self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count as usize) - .map(|position| ${orientation[1]}Position(position.mPosition.m${orientation[0].upper()}Position.into())) + .take(self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count as usize) + .map(|position| position.mPosition.m${orientation.upper()}Position.into()) .collect() ) } @@ -2778,7 +2776,7 @@ fn static_assert() { self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count = v.len() as u32; for (servo, geckolayer) in v.zip(self.gecko.${image_layers_field} .mLayers.iter_mut()) { - geckolayer.mPosition.m${orientation[0].upper()}Position = servo.0.into(); + geckolayer.mPosition.m${orientation[0].upper()}Position = servo.into(); } } % endfor diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index fcc963d2d2a..9d75c63d7eb 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -41,7 +41,6 @@ use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone use values::computed::{BorderRadiusSize, ClipRect}; use values::computed::{CalcLengthOrPercentage, Context, LengthOrPercentage}; use values::computed::{MaxLength, MinLength}; -use values::computed::position::{HorizontalPosition, VerticalPosition}; use values::computed::ToComputedValue; use values::generics::position as generic_position; @@ -655,6 +654,7 @@ pub trait Animatable: Sized { /// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list pub trait RepeatableListAnimatable: Animatable {} +impl RepeatableListAnimatable for LengthOrPercentage {} impl RepeatableListAnimatable for Either {} impl Animatable for SmallVec<[T; 1]> { @@ -1336,46 +1336,6 @@ impl Animatable for generic_position::Position RepeatableListAnimatable for generic_position::Position where H: RepeatableListAnimatable, V: RepeatableListAnimatable {} -/// https://drafts.csswg.org/css-transitions/#animtype-simple-list -impl Animatable for HorizontalPosition { - #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { - self.0.interpolate(&other.0, progress).map(generic_position::HorizontalPosition) - } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.0.compute_distance(&other.0) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - self.0.compute_squared_distance(&other.0) - } -} - -impl RepeatableListAnimatable for HorizontalPosition {} - -/// https://drafts.csswg.org/css-transitions/#animtype-simple-list -impl Animatable for VerticalPosition { - #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { - self.0.interpolate(&other.0, progress).map(generic_position::VerticalPosition) - } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.0.compute_distance(&other.0) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - self.0.compute_squared_distance(&other.0) - } -} - -impl RepeatableListAnimatable for VerticalPosition {} - /// https://drafts.csswg.org/css-transitions/#animtype-rect impl Animatable for ClipRect { #[inline] diff --git a/components/style/properties/longhand/background.mako.rs b/components/style/properties/longhand/background.mako.rs index 50e2ce4e9bd..b0c998d59fd 100644 --- a/components/style/properties/longhand/background.mako.rs +++ b/components/style/properties/longhand/background.mako.rs @@ -20,38 +20,13 @@ ${helpers.predefined_type("background-image", "LayerImage", animation_value_type="none", has_uncacheable_values="True" if product == "gecko" else "False")} -<%helpers:predefined_type name="background-position-x" type="position::HorizontalPosition" - initial_value="computed::position::HorizontalPosition::zero()" - initial_specified_value="specified::position::HorizontalPosition::left()" - spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-x" - animation_value_type="ComputedValue" vector="True" delegate_animate="True"> - #[inline] - /// Get the initial value for horizontal position. - pub fn get_initial_position_value() -> SpecifiedValue { - use values::generics::position::{HorizontalPosition, PositionValue}; - use values::specified::{LengthOrPercentage, Percentage}; - HorizontalPosition(PositionValue { - keyword: None, - position: Some(LengthOrPercentage::Percentage(Percentage(0.0))), - }) - } - - -<%helpers:predefined_type name="background-position-y" type="position::VerticalPosition" - initial_value="computed::position::VerticalPosition::zero()" - initial_specified_value="specified::position::VerticalPosition::top()" - spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-y" - animation_value_type="ComputedValue" vector="True" delegate_animate="True"> - /// Get the initial value for vertical position. - pub fn get_initial_position_value() -> SpecifiedValue { - use values::generics::position::{VerticalPosition, PositionValue}; - use values::specified::{LengthOrPercentage, Percentage}; - VerticalPosition(PositionValue { - keyword: None, - position: Some(LengthOrPercentage::Percentage(Percentage(0.0))), - }) - } - +% for (axis, direction, initial) in [("x", "Horizontal", "left"), ("y", "Vertical", "top")]: + ${helpers.predefined_type("background-position-" + axis, "position::" + direction + "Position", + initial_value="computed::LengthOrPercentage::zero()", + initial_specified_value="SpecifiedValue::initial_specified_value()", + spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis, + animation_value_type="ComputedValue", vector=True, delegate_animate=True)} +% endfor <%helpers:vector_longhand name="background-repeat" animation_value_type="none" spec="https://drafts.csswg.org/css-backgrounds/#the-background-repeat"> diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index b9a451b1571..4927c807d2f 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -2143,11 +2143,11 @@ ${helpers.predefined_type("perspective", animation_value_type="ComputedValue")} ${helpers.predefined_type("perspective-origin", - "position::OriginPosition", - "computed::position::OriginPosition::center()", + "position::Position", + "computed::position::Position::center()", boxed="True", extra_prefixes="moz webkit", - spec="https://drafts.csswg.org/css-transforms/#perspective-origin-property", + spec="https://drafts.csswg.org/css-transforms-2/#perspective-origin-property", animation_value_type="ComputedValue")} ${helpers.single_keyword("backface-visibility", diff --git a/components/style/properties/longhand/svg.mako.rs b/components/style/properties/longhand/svg.mako.rs index 104929fff77..c1327a6ca2e 100644 --- a/components/style/properties/longhand/svg.mako.rs +++ b/components/style/properties/longhand/svg.mako.rs @@ -90,49 +90,14 @@ ${helpers.single_keyword("mask-mode", } -<%helpers:vector_longhand name="mask-position-x" products="gecko" - animation_value_type="ComputedValue" extra_prefixes="webkit" - spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position"> - pub use properties::longhands::background_position_x::single_value::get_initial_value; - pub use properties::longhands::background_position_x::single_value::get_initial_position_value; - pub use properties::longhands::background_position_x::single_value::get_initial_specified_value; - pub use properties::longhands::background_position_x::single_value::parse; - pub use properties::longhands::background_position_x::single_value::SpecifiedValue; - pub use properties::longhands::background_position_x::single_value::computed_value; - use properties::animated_properties::{Animatable, RepeatableListAnimatable}; - use properties::longhands::mask_position_x::computed_value::T as MaskPositionX; - - impl Animatable for MaskPositionX { - #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { - Ok(MaskPositionX(try!(self.0.interpolate(&other.0, progress)))) - } - } - - impl RepeatableListAnimatable for MaskPositionX {} - - -<%helpers:vector_longhand name="mask-position-y" products="gecko" - animation_value_type="ComputedValue" extra_prefixes="webkit" - spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position"> - pub use properties::longhands::background_position_y::single_value::get_initial_value; - pub use properties::longhands::background_position_y::single_value::get_initial_position_value; - pub use properties::longhands::background_position_y::single_value::get_initial_specified_value; - pub use properties::longhands::background_position_y::single_value::parse; - pub use properties::longhands::background_position_y::single_value::SpecifiedValue; - pub use properties::longhands::background_position_y::single_value::computed_value; - use properties::animated_properties::{Animatable, RepeatableListAnimatable}; - use properties::longhands::mask_position_y::computed_value::T as MaskPositionY; - - impl Animatable for MaskPositionY { - #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { - Ok(MaskPositionY(try!(self.0.interpolate(&other.0, progress)))) - } - } - - impl RepeatableListAnimatable for MaskPositionY {} - +% for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]: + ${helpers.predefined_type("mask-position-" + axis, "position::" + direction + "Position", + products="gecko", extra_prefixes="webkit", + initial_value="computed::LengthOrPercentage::zero()", + initial_specified_value="specified::PositionComponent::Center", + spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position", + animation_value_type="ComputedValue", vector=True, delegate_animate=True)} +% endfor ${helpers.single_keyword("mask-clip", "border-box content-box padding-box", diff --git a/components/style/properties/shorthand/background.mako.rs b/components/style/properties/shorthand/background.mako.rs index 21a75db2693..7732a84ff73 100644 --- a/components/style/properties/shorthand/background.mako.rs +++ b/components/style/properties/shorthand/background.mako.rs @@ -15,7 +15,7 @@ use properties::longhands::background_clip; use properties::longhands::background_clip::single_value::computed_value::T as Clip; use properties::longhands::background_origin::single_value::computed_value::T as Origin; - use values::specified::position::Position; + use values::specified::{Position, PositionComponent}; use parser::Parse; impl From for background_clip::single_value::SpecifiedValue { @@ -39,7 +39,7 @@ let mut background_${name} = background_${name}::SpecifiedValue(Vec::new()); % endfor try!(input.parse_comma_separated(|input| { - % for name in "image position_x position_y repeat size attachment origin clip".split(): + % for name in "image position repeat size attachment origin clip".split(): let mut ${name} = None; % endfor loop { @@ -52,10 +52,9 @@ return Err(()) } } - if position_x.is_none() && position_y.is_none() { + if position.is_none() { if let Ok(value) = input.try(|input| Position::parse(context, input)) { - position_x = Some(value.horizontal); - position_y = Some(value.vertical); + position = Some(value); // Parse background size, if applicable. size = input.try(|input| { @@ -83,22 +82,17 @@ } } let mut any = false; - % for name in "image position_x position_y repeat size attachment origin clip".split(): + % for name in "image position repeat size attachment origin clip".split(): any = any || ${name}.is_some(); % endfor any = any || background_color.is_some(); if any { - 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 + if let Some(position) = position { + background_position_x.0.push(position.horizontal); + background_position_y.0.push(position.vertical); } else { - % for name in "position_x position_y".split(): - background_${name}.0.push(background_${name}::single_value - ::get_initial_position_value()); - % endfor + background_position_x.0.push(PositionComponent::zero()); + background_position_y.0.push(PositionComponent::zero()); } % for name in "image repeat size attachment origin clip".split(): if let Some(bg_${name}) = ${name} { @@ -193,7 +187,7 @@ <%helpers:shorthand name="background-position" sub_properties="background-position-x background-position-y" spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position"> - use properties::longhands::{background_position_x,background_position_y}; + use properties::longhands::{background_position_x, background_position_y}; use values::specified::AllowQuirks; use values::specified::position::Position; @@ -202,18 +196,13 @@ 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_quirky(context, input, AllowQuirks::Yes)) { - position_x.0.push(value.horizontal); - position_y.0.push(value.vertical); - any = true; - continue - } - break - } + input.parse_comma_separated(|input| { + let value = Position::parse_quirky(context, input, AllowQuirks::Yes)?; + position_x.0.push(value.horizontal); + position_y.0.push(value.vertical); + any = true; Ok(()) - })); + })?; if !any { return Err(()); } diff --git a/components/style/properties/shorthand/mask.mako.rs b/components/style/properties/shorthand/mask.mako.rs index 8096f223757..e9d03035964 100644 --- a/components/style/properties/shorthand/mask.mako.rs +++ b/components/style/properties/shorthand/mask.mako.rs @@ -11,7 +11,7 @@ use properties::longhands::{mask_mode, mask_repeat, mask_clip, mask_origin, mask_composite, mask_position_x, mask_position_y}; use properties::longhands::{mask_size, mask_image}; - use values::specified::position::Position; + use values::specified::{Position, PositionComponent}; use parser::Parse; impl From for mask_clip::single_value::SpecifiedValue { @@ -41,7 +41,7 @@ % endfor try!(input.parse_comma_separated(|input| { - % for name in "image mode position_x position_y size repeat origin clip composite".split(): + % for name in "image mode position size repeat origin clip composite".split(): let mut ${name} = None; % endfor loop { @@ -52,10 +52,9 @@ continue } } - if position_x.is_none() && position_y.is_none() { + if position.is_none() { if let Ok(value) = input.try(|input| Position::parse(context, input)) { - position_x = Some(value.horizontal); - position_y = Some(value.vertical); + position = Some(value); // Parse mask size, if applicable. size = input.try(|input| { @@ -83,21 +82,16 @@ } } let mut any = false; - % for name in "image mode position_x position_y size repeat origin clip composite".split(): + % for name in "image mode position size repeat origin clip composite".split(): any = any || ${name}.is_some(); % endfor if any { - if position_x.is_some() || position_y.is_some() { - % for name in "position_x position_y".split(): - if let Some(bg_${name}) = ${name} { - mask_${name}.0.push(bg_${name}); - } - % endfor + if let Some(position) = position { + mask_position_x.0.push(position.horizontal); + mask_position_y.0.push(position.vertical); } else { - % for name in "position_x position_y".split(): - mask_${name}.0.push(mask_${name}::single_value - ::get_initial_position_value()); - % endfor + mask_position_x.0.push(PositionComponent::zero()); + mask_position_y.0.push(PositionComponent::zero()); } % for name in "image mode size repeat origin clip composite".split(): if let Some(m_${name}) = ${name} { @@ -191,18 +185,13 @@ let mut position_y = mask_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 - } + input.parse_comma_separated(|input| { + let value = Position::parse(context, input)?; + position_x.0.push(value.horizontal); + position_y.0.push(value.vertical); + any = true; Ok(()) - })); + })?; if any == false { return Err(()); } diff --git a/components/style/values/computed/position.rs b/components/style/values/computed/position.rs index d30e911f9fa..97f14b73f04 100644 --- a/components/style/values/computed/position.rs +++ b/components/style/values/computed/position.rs @@ -10,39 +10,28 @@ use std::fmt; use style_traits::ToCss; use values::computed::LengthOrPercentage; -use values::generics::position::{Position as GenericPosition, PositionWithKeyword}; -use values::generics::position::HorizontalPosition as GenericHorizontalPosition; -use values::generics::position::VerticalPosition as GenericVerticalPosition; +use values::generics::position::Position as GenericPosition; /// The computed value of a CSS `` -pub type Position = PositionWithKeyword; +pub type Position = GenericPosition; -impl Copy for Position {} +/// The computed value of a CSS horizontal position. +pub type HorizontalPosition = LengthOrPercentage; -/// The computed value for `` values without a keyword. -pub type OriginPosition = GenericPosition; - -impl Copy for OriginPosition {} - -impl OriginPosition { - #[inline] - /// The initial value for `perspective-origin` - pub fn center() -> OriginPosition { - GenericPosition { - horizontal: LengthOrPercentage::Percentage(0.5), - vertical: LengthOrPercentage::Percentage(0.5), - } - } -} +/// The computed value of a CSS vertical position. +pub type VerticalPosition = LengthOrPercentage; impl Position { + /// `50% 50%` + #[inline] + pub fn center() -> Self { + Self::new(LengthOrPercentage::Percentage(0.5), LengthOrPercentage::Percentage(0.5)) + } + + /// `0% 0%` #[inline] - /// Construct a position at (0, 0) pub fn zero() -> Self { - Position { - horizontal: GenericHorizontalPosition(LengthOrPercentage::zero()), - vertical: GenericVerticalPosition(LengthOrPercentage::zero()), - } + Self::new(LengthOrPercentage::zero(), LengthOrPercentage::zero()) } } @@ -53,29 +42,3 @@ impl ToCss for Position { self.vertical.to_css(dest) } } - -/// The computed value of a horizontal `` -pub type HorizontalPosition = GenericHorizontalPosition; - -impl Copy for HorizontalPosition {} - -impl HorizontalPosition { - #[inline] - /// Create a zero position value. - pub fn zero() -> HorizontalPosition { - GenericHorizontalPosition(LengthOrPercentage::Percentage(0.0)) - } -} - -/// The computed value of a vertical `` -pub type VerticalPosition = GenericVerticalPosition; - -impl Copy for VerticalPosition {} - -impl VerticalPosition { - #[inline] - /// Create a zero position value. - pub fn zero() -> VerticalPosition { - GenericVerticalPosition(LengthOrPercentage::Percentage(0.0)) - } -} diff --git a/components/style/values/generics/position.rs b/components/style/values/generics/position.rs index d15198b638e..1b805b8ff08 100644 --- a/components/style/values/generics/position.rs +++ b/components/style/values/generics/position.rs @@ -5,231 +5,12 @@ //! Generic types for CSS handling of specified and computed values of //! [`position`](https://drafts.csswg.org/css-backgrounds-3/#position) -use cssparser::Parser; -use parser::{Parse, ParserContext}; -use std::fmt; -use style_traits::ToCss; use values::HasViewportPercentage; -use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue}; -use values::specified::{LengthOrPercentage, Percentage}; +use values::computed::{Context, ToComputedValue}; -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 -} - -add_impls_for_keyword_enum!(Keyword); - -impl Keyword { - #[inline] - /// The defaults for position keywords are `left` and `top` (`x-start` and `y-start` for logical). - /// This method checks whether this keyword indicates their opposite sides. See the - /// `ToComputedValue` impl on `HorizontalPosition` and `VerticalPosition` for its use case. - pub fn is_other_side(&self) -> bool { - if self.is_horizontal() || self.is_logical_x() { - matches!(*self, Keyword::Right | Keyword::XEnd) - } else { - matches!(*self, Keyword::Bottom | Keyword::YEnd) - } - } - - #[inline] - /// Check whether this is a keyword for horizontal position. - pub fn is_horizontal(&self) -> bool { - matches!(*self, Keyword::Left | Keyword::Right) - } - - #[inline] - /// Check whether this is a keyword for vertical position. - pub fn is_vertical(&self) -> bool { - matches!(*self, Keyword::Top | Keyword::Bottom) - } - - #[inline] - /// Check whether this is a horizontal logical keyword. - pub fn is_logical_x(&self) -> bool { - matches!(*self, Keyword::XStart | Keyword::XEnd) - } - - #[inline] - /// Check whether this is a vertical logical keyword. - pub fn is_logical_y(&self) -> bool { - matches!(*self, Keyword::YStart | Keyword::YEnd) - } - - #[inline] - /// Check whether this is a logical keyword. - pub fn is_logical(&self) -> bool { - self.is_logical_x() || self.is_logical_y() - } -} - -impl From for LengthOrPercentage { - fn from(val: Keyword) -> LengthOrPercentage { - match val { - 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)), - } - } -} - -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -/// A generic type for representing horizontal or vertical `` value. -pub struct PositionValue { - /// Even though this is generic, it's always a `` value. - pub position: Option, - /// A position keyword. - pub keyword: Option, -} - -impl HasViewportPercentage for PositionValue { - #[inline] - fn has_viewport_percentage(&self) -> bool { - self.position.as_ref().map_or(false, |pos| pos.has_viewport_percentage()) - } -} - -impl PositionValue { - /// Internal parsing function which (after parsing) checks the keyword with the - /// given function. - pub fn parse_internal(context: &ParserContext, input: &mut Parser, - mut is_allowed_keyword: F) -> Result, ()> - where F: FnMut(Keyword) -> bool - { - let (mut pos, mut keyword) = (None, None); - for _ in 0..2 { - if let Ok(l) = input.try(|i| L::parse(context, i)) { - if pos.is_some() { - return Err(()) - } - - pos = Some(l); - } - - if let Ok(k) = input.try(Keyword::parse) { - if keyword.is_some() || !is_allowed_keyword(k) { - return Err(()) - } - - keyword = Some(k); - } - } - - if pos.is_some() { - if let Some(Keyword::Center) = keyword { - return Err(()) // "center" and is not allowed - } - } else if keyword.is_none() { - return Err(()) // at least one value is necessary - } - - Ok(PositionValue { - position: pos, - keyword: keyword, - }) - } -} - -impl ToCss for PositionValue { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if let Some(keyword) = self.keyword { - keyword.to_css(dest)?; - } - - if let Some(ref position) = self.position { - if self.keyword.is_some() { - dest.write_str(" ")?; - } - - position.to_css(dest)?; - } - - Ok(()) - } -} - -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -/// A generic type for representing horizontal `` -pub struct HorizontalPosition(pub L); - -impl ToCss for HorizontalPosition { - #[inline] - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - self.0.to_css(dest) - } -} - -impl HasViewportPercentage for HorizontalPosition { - #[inline] - fn has_viewport_percentage(&self) -> bool { - self.0.has_viewport_percentage() - } -} - -impl Parse for HorizontalPosition> { - #[inline] - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - PositionValue::parse_internal(context, input, |keyword| { - matches!{ keyword, - Keyword::Left | Keyword::Right | Keyword::Center | - Keyword::XStart | Keyword::XEnd - } - }).map(HorizontalPosition) - } -} - -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -/// A generic type for representing vertical `` -pub struct VerticalPosition(pub L); - -impl ToCss for VerticalPosition { - #[inline] - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - self.0.to_css(dest) - } -} - -impl HasViewportPercentage for VerticalPosition { - #[inline] - fn has_viewport_percentage(&self) -> bool { - self.0.has_viewport_percentage() - } -} - -impl Parse for VerticalPosition> { - #[inline] - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - PositionValue::parse_internal(context, input, |keyword| { - matches!{ keyword, - Keyword::Top | Keyword::Bottom | Keyword::Center | - Keyword::YStart | Keyword::YEnd - } - }).map(VerticalPosition) - } -} - -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] /// A generic type for representing a CSS [position](https://drafts.csswg.org/css-values/#position). -/// -/// Note that the horizontal and vertical positions aren't really different types. -/// They're just unit struct wrappers over `LengthOrPercentage`. They should be different -/// because they allow different keywords (for e.g., vertical position doesn't allow -/// `right` or `left` keywords and vice versa). pub struct Position { /// The horizontal component of position. pub horizontal: H, @@ -237,8 +18,15 @@ pub struct Position { pub vertical: V, } -/// A generic type for representing positions with keywords. -pub type PositionWithKeyword = Position, VerticalPosition>; +impl Position { + /// Returns a new position. + pub fn new(horizontal: H, vertical: V) -> Self { + Self { + horizontal: horizontal, + vertical: vertical, + } + } +} impl HasViewportPercentage for Position { #[inline] @@ -247,42 +35,9 @@ impl HasViewportPercentage f } } -impl ToCss for Position { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - self.horizontal.to_css(dest)?; - dest.write_str(" ")?; - self.vertical.to_css(dest) - } -} - -impl ToCss for PositionWithKeyword> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - macro_rules! to_css_with_keyword { - ($pos:expr, $default:expr) => { - $pos.keyword.unwrap_or($default).to_css(dest)?; - if let Some(ref position) = $pos.position { - dest.write_str(" ")?; - position.to_css(dest)?; - } - } - } - - if (self.horizontal.0.keyword.is_some() && self.horizontal.0.position.is_some()) || - (self.vertical.0.keyword.is_some() && self.vertical.0.position.is_some()) { - to_css_with_keyword!(self.horizontal.0, Keyword::Left); - dest.write_str(" ")?; - to_css_with_keyword!(self.vertical.0, Keyword::Top); - return Ok(()) - } - - self.horizontal.to_css(dest)?; - dest.write_str(" ")?; - self.vertical.to_css(dest) - } -} - impl ToComputedValue for Position { - type ComputedValue = Position; + type ComputedValue = Position<::ComputedValue, + ::ComputedValue>; #[inline] fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { @@ -294,7 +49,7 @@ impl ToComputedValue for Position #[inline] fn from_computed_value(computed: &Self::ComputedValue) -> Self { - Position { + Self { horizontal: ToComputedValue::from_computed_value(&computed.horizontal), vertical: ToComputedValue::from_computed_value(&computed.vertical), } diff --git a/components/style/values/specified/basic_shape.rs b/components/style/values/specified/basic_shape.rs index 9d1176dad70..94d006f6033 100644 --- a/components/style/values/specified/basic_shape.rs +++ b/components/style/values/specified/basic_shape.rs @@ -11,6 +11,7 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; use properties::shorthands::parse_four_sides; use std::ascii::AsciiExt; +use std::borrow::Cow; use std::fmt; use style_traits::ToCss; use values::HasViewportPercentage; @@ -19,8 +20,8 @@ use values::computed::basic_shape as computed_basic_shape; use values::generics::BorderRadiusSize; use values::generics::basic_shape::{BorderRadius as GenericBorderRadius, ShapeRadius as GenericShapeRadius}; use values::generics::basic_shape::{InsetRect as GenericInsetRect, Polygon as GenericPolygon, ShapeSource}; -use values::specified::{LengthOrPercentage, Percentage}; -use values::specified::position::{Keyword, Position}; +use values::specified::{LengthOrPercentage, Percentage, Position, PositionComponent}; +use values::specified::position::Side; /// The specified value used by `clip-path` pub type ShapeWithGeometryBox = ShapeSource; @@ -138,75 +139,62 @@ impl Parse for InsetRect { fn serialize_basicshape_position(position: &Position, dest: &mut W) -> fmt::Result where W: fmt::Write { - // 0 length should be replaced with 0% - fn replace_with_percent(input: LengthOrPercentage) -> LengthOrPercentage { - match input { - LengthOrPercentage::Length(ref l) if l.is_zero() => - LengthOrPercentage::Percentage(Percentage(0.0)), - _ => input - } - } - - // keyword-percentage pairs can be folded into a single percentage - fn fold_keyword(keyword: Option, - length: Option) -> Option { - let is_length_none = length.is_none(); - let pc = match length.map(replace_with_percent) { - Some(LengthOrPercentage::Percentage(pc)) => pc, - None => Percentage(0.0), // unspecified length = 0% - _ => return None - }; - - let percent = match keyword { - Some(Keyword::Center) => { - assert!(is_length_none); // center cannot pair with lengths - Percentage(0.5) + fn to_keyword_and_lop(component: &PositionComponent) -> (S, Cow) + where S: Copy + Side + { + match *component { + PositionComponent::Center => { + (S::start(), Cow::Owned(LengthOrPercentage::Percentage(Percentage(0.5)))) }, - 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)) + PositionComponent::Side(keyword, None) => { + // left | top => 0% + // right | bottom => 100% + let p = if keyword.is_start() { 0. } else { 1. }; + (S::start(), Cow::Owned(LengthOrPercentage::Percentage(Percentage(p)))) + }, + PositionComponent::Side(keyword, Some(ref lop)) if !keyword.is_start() => { + if let LengthOrPercentage::Percentage(p) = *to_non_zero_length(lop) { + (S::start(), Cow::Owned(LengthOrPercentage::Percentage(Percentage(1. - p.0)))) + } else { + (keyword, Cow::Borrowed(lop)) + } + }, + PositionComponent::Length(ref lop) | + PositionComponent::Side(_, Some(ref lop)) => { + (S::start(), to_non_zero_length(lop)) + }, + } } - fn serialize_position_pair(x: LengthOrPercentage, y: LengthOrPercentage, - dest: &mut W) -> fmt::Result where W: fmt::Write { - replace_with_percent(x).to_css(dest)?; - dest.write_str(" ")?; - replace_with_percent(y).to_css(dest) - } - - match (position.horizontal.0.keyword, position.horizontal.0.position.clone(), - position.vertical.0.keyword, position.vertical.0.position.clone()) { - (Some(hk), None, Some(vk), None) => { - // two keywords: serialize as two lengths - serialize_position_pair(hk.into(), vk.into(), dest) - } - (None, Some(hp), None, Some(vp)) => { - // two lengths: just serialize regularly - serialize_position_pair(hp, vp, dest) - } - (hk, hp, vk, vp) => { - // only fold if both fold; the three-value form isn't - // allowed here. - if let (Some(x), Some(y)) = (fold_keyword(hk, hp.clone()), - fold_keyword(vk, vp.clone())) { - serialize_position_pair(x, y, dest) - } else { - // We failed to reduce it to a two-value form, - // so we expand it to 4-value - let zero = LengthOrPercentage::Percentage(Percentage(0.0)); - hk.unwrap_or(Keyword::Left).to_css(dest)?; - dest.write_str(" ")?; - replace_with_percent(hp.unwrap_or(zero.clone())).to_css(dest)?; - dest.write_str(" ")?; - vk.unwrap_or(Keyword::Top).to_css(dest)?; - dest.write_str(" ")?; - replace_with_percent(vp.unwrap_or(zero)).to_css(dest) + fn to_non_zero_length(lop: &LengthOrPercentage) -> Cow { + match *lop { + LengthOrPercentage::Length(ref l) if l.is_zero() => { + Cow::Owned(LengthOrPercentage::Percentage(Percentage(0.))) + }, + _ => { + Cow::Borrowed(lop) } } } + + fn write_pair(a: &A, b: &B, dest: &mut W) -> fmt::Result + where A: ToCss, B: ToCss, W: fmt::Write + { + a.to_css(dest)?; + dest.write_str(" ")?; + b.to_css(dest) + } + + let (x_pos, x_lop) = to_keyword_and_lop(&position.horizontal); + let (y_pos, y_lop) = to_keyword_and_lop(&position.vertical); + + if x_pos.is_start() && y_pos.is_start() { + return write_pair(&*x_lop, &*y_lop, dest); + } + + write_pair(&x_pos, &*x_lop, dest)?; + dest.write_str(" ")?; + write_pair(&y_pos, &*y_lop, dest) } #[derive(Clone, PartialEq, Debug)] diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 3062e3bfff0..a14b6ca2e62 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -36,7 +36,7 @@ pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWi pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; pub use self::length::{LengthOrPercentageOrNone, LengthOrPercentageOrAutoOrContent, NoCalcLength}; pub use self::length::{MaxLength, MinLength}; -pub use self::position::{HorizontalPosition, Position, VerticalPosition}; +pub use self::position::{Position, PositionComponent}; #[cfg(feature = "gecko")] pub mod align; diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs index dfa05b36609..678d2a8777d 100644 --- a/components/style/values/specified/position.rs +++ b/components/style/values/specified/position.rs @@ -7,278 +7,297 @@ //! //! [position]: https://drafts.csswg.org/css-backgrounds-3/#position -use app_units::Au; use cssparser::Parser; use parser::{Parse, ParserContext}; -use properties::longhands::parse_origin; -use std::mem; -use values::Either; -use values::computed::{CalcLengthOrPercentage, Context}; +use std::fmt; +use style_traits::ToCss; +use values::HasViewportPercentage; +use values::computed::{CalcLengthOrPercentage, ComputedValueAsSpecified, Context}; use values::computed::{LengthOrPercentage as ComputedLengthOrPercentage, ToComputedValue}; -use values::computed::position as computed_position; -use values::generics::position::{Position as GenericPosition, PositionValue, PositionWithKeyword}; -use values::generics::position::HorizontalPosition as GenericHorizontalPosition; -use values::generics::position::VerticalPosition as GenericVerticalPosition; +use values::generics::position::Position as GenericPosition; use values::specified::{AllowQuirks, LengthOrPercentage, Percentage}; -pub use values::generics::position::Keyword; - /// The specified value of a CSS `` -pub type Position = PositionWithKeyword>; +pub type Position = GenericPosition; -/// The specified value for `` values without a keyword. -pub type OriginPosition = GenericPosition; +/// The specified value of a horizontal position. +pub type HorizontalPosition = PositionComponent; -impl Parse for OriginPosition { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - let result = parse_origin(context, input)?; - match result.depth { - Some(_) => Err(()), - None => Ok(GenericPosition { - horizontal: result.horizontal.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))), - vertical: result.vertical.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))), - }) - } - } +/// The specified value of a vertical position. +pub type VerticalPosition = PositionComponent; + +/// The specified value of a component of a CSS ``. +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Debug, PartialEq)] +pub enum PositionComponent { + /// `center` + Center, + /// `` + Length(LengthOrPercentage), + /// ` ?` + Side(S, Option), } -type PositionComponent = Either; - -impl Position { - /// Create a new position value from either a length or a keyword. - pub fn from_components(mut first_position: Option, - mut second_position: Option, - first_keyword: Option, - second_keyword: Option) -> Result { - // Unwrap for checking if values are at right place. - let first_key = first_keyword.clone().unwrap_or(Either::Second(Keyword::Left)); - let second_key = second_keyword.clone().unwrap_or(Either::Second(Keyword::Top)); - - let (horiz_keyword, vert_keyword) = match (&first_key, &second_key) { - // Check if a position is specified after center keyword. - (&Either::Second(Keyword::Center), _) if first_position.is_some() => return Err(()), - (_, &Either::Second(Keyword::Center)) if second_position.is_some() => return Err(()), - - // Check first and second keywords for both 2 and 4 value positions. - - // FIXME(canaltinova): Allow logical keywords for Position. They are not in current spec yet. - (&Either::Second(k), _) if k.is_logical() => return Err(()), - (_, &Either::Second(k)) if k.is_logical() => return Err(()), - - // Don't allow two vertical keywords or two horizontal keywords. - (&Either::Second(k1), &Either::Second(k2)) - if (k1.is_horizontal() && k2.is_horizontal()) || (k1.is_vertical() && k2.is_vertical()) => - return Err(()), - - // Also don't allow values in the wrong position - (&Either::First(_), &Either::Second(k)) if k.is_horizontal() => return Err(()), - (&Either::Second(k), &Either::First(_)) if k.is_vertical() => return Err(()), - - // Swap if both are keywords and vertical precedes horizontal. - (&Either::Second(k1), &Either::Second(k2)) - if (k1.is_vertical() && k2.is_horizontal()) || (k1.is_vertical() && k2 == Keyword::Center) || - (k1 == Keyword::Center && k2.is_horizontal()) => { - mem::swap(&mut first_position, &mut second_position); - (second_keyword, first_keyword) - }, - - // By default, horizontal is first. - _ => (first_keyword, second_keyword), - }; - - let (mut h_pos, mut h_key, mut v_pos, mut v_key) = (None, None, None, None); - if let Some(Either::First(l)) = first_position { - h_pos = Some(l); - } - - if let Some(Either::First(l)) = second_position { - v_pos = Some(l); - } - - if let Some(Either::Second(k)) = horiz_keyword { - h_key = Some(k); - } - - if let Some(Either::Second(k)) = vert_keyword { - v_key = Some(k); - } - - Ok(Position { - horizontal: GenericHorizontalPosition(PositionValue { - keyword: h_key, - position: h_pos, - }), - vertical: GenericVerticalPosition(PositionValue { - keyword: v_key, - position: v_pos, - }), - }) - } - - /// Returns a "centered" position, as in "center center". - pub fn center() -> Position { - Position { - horizontal: GenericHorizontalPosition(PositionValue { - keyword: Some(Keyword::Center), - position: None, - }), - vertical: GenericVerticalPosition(PositionValue { - keyword: Some(Keyword::Center), - position: None, - }), - } - } +define_css_keyword_enum! { X: + "left" => Left, + "right" => Right, } +add_impls_for_keyword_enum!(X); + +define_css_keyword_enum! { Y: + "top" => Top, + "bottom" => Bottom, +} +add_impls_for_keyword_enum!(Y); impl Parse for Position { fn parse(context: &ParserContext, input: &mut Parser) -> Result { - Position::parse_quirky(context, input, AllowQuirks::No) + Self::parse_quirky(context, input, AllowQuirks::No) } } impl Position { - /// Parses, with quirks. + /// Parses a ``, with quirks. pub fn parse_quirky(context: &ParserContext, input: &mut Parser, allow_quirks: AllowQuirks) -> Result { - let first = input.try(|i| PositionComponent::parse_quirky(context, i, allow_quirks))?; - let second = input.try(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) - .unwrap_or(Either::Second(Keyword::Center)); - - if let Ok(third) = input.try(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) { - // There's a 3rd value. - if let Ok(fourth) = input.try(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) { - // There's a 4th value. - Position::from_components(Some(second), Some(fourth), Some(first), Some(third)) - } else { - // For 3 value background position, there are several options. - if let Either::First(_) = first { - return Err(()) // must be preceded by + match input.try(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) { + Ok(x_pos @ PositionComponent::Center) => { + if let Ok(y_pos) = input.try(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) { + return Ok(Self::new(x_pos, y_pos)); } - - // only 3 values. - match (&second, &third) { - (&Either::First(_), &Either::First(_)) => Err(()), - // "keyword length keyword" - (&Either::First(_), _) => Position::from_components(Some(second), None, - Some(first), Some(third)), - // "keyword keyword length" - _ => Position::from_components(None, Some(third), Some(first), Some(second)), - } - } - } else { - // only 2 values. - match (&first, &second) { - (&Either::First(_), &Either::First(_)) => - Position::from_components(Some(first), Some(second), None, None), - (&Either::First(_), &Either::Second(_)) => - Position::from_components(Some(first), None, None, Some(second)), - (&Either::Second(_), &Either::First(_)) => - Position::from_components(None, Some(second), Some(first), None), - (&Either::Second(_), &Either::Second(_)) => - Position::from_components(None, None, Some(first), Some(second)), - } - } - } -} - -impl PositionComponent { - /// Parses, with quirks. - fn parse_quirky(context: &ParserContext, - input: &mut Parser, - allow_quirks: AllowQuirks) -> Result { - input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)) - .map(Either::First) - .or_else(|()| input.try(Keyword::parse).map(Either::Second)) - } -} - -impl PositionValue { - /// Generic function for the computed value of a position. - fn computed_value(&self, context: &Context) -> ComputedLengthOrPercentage { - match self.keyword { - Some(Keyword::Center) => ComputedLengthOrPercentage::Percentage(0.5), - Some(k) if k.is_other_side() => match self.position { - Some(ref x) => { - let (length, percentage) = match *x { - LengthOrPercentage::Percentage(Percentage(y)) => (Au(0), Some(1.0 - y)), - LengthOrPercentage::Length(ref y) => (-y.to_computed_value(context), Some(1.0)), - _ => (Au(0), None), - }; - - ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage { - length: length, - percentage: percentage - }) - }, - None => ComputedLengthOrPercentage::Percentage(1.0), + let x_pos = input + .try(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) + .unwrap_or(x_pos); + let y_pos = PositionComponent::Center; + return Ok(Self::new(x_pos, y_pos)); + }, + Ok(PositionComponent::Side(x_keyword, lop)) => { + if input.try(|i| i.expect_ident_matching("center")).is_ok() { + let x_pos = PositionComponent::Side(x_keyword, lop); + let y_pos = PositionComponent::Center; + return Ok(Self::new(x_pos, y_pos)); + } + if let Ok(y_keyword) = input.try(Y::parse) { + let y_lop = input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok(); + let x_pos = PositionComponent::Side(x_keyword, lop); + let y_pos = PositionComponent::Side(y_keyword, y_lop); + return Ok(Self::new(x_pos, y_pos)); + } + let x_pos = PositionComponent::Side(x_keyword, None); + let y_pos = lop.map_or(PositionComponent::Center, PositionComponent::Length); + return Ok(Self::new(x_pos, y_pos)); + }, + Ok(x_pos @ PositionComponent::Length(_)) => { + if let Ok(y_keyword) = input.try(Y::parse) { + let y_pos = PositionComponent::Side(y_keyword, None); + return Ok(Self::new(x_pos, y_pos)); + } + if let Ok(y_lop) = input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)) { + let y_pos = PositionComponent::Length(y_lop); + return Ok(Self::new(x_pos, y_pos)); + } + let y_pos = PositionComponent::Center; + let _ = input.try(|i| i.expect_ident_matching("center")); + return Ok(Self::new(x_pos, y_pos)); + }, + Err(_) => {}, + } + let y_keyword = Y::parse(input)?; + let lop_and_x_pos: Result<_, ()> = input.try(|i| { + let y_lop = i.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok(); + if let Ok(x_keyword) = i.try(X::parse) { + let x_lop = i.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok(); + let x_pos = PositionComponent::Side(x_keyword, x_lop); + return Ok((y_lop, x_pos)); + } + i.expect_ident_matching("center")?; + let x_pos = PositionComponent::Center; + Ok((y_lop, x_pos)) + }); + if let Ok((y_lop, x_pos)) = lop_and_x_pos { + let y_pos = PositionComponent::Side(y_keyword, y_lop); + return Ok(Self::new(x_pos, y_pos)); + } + let x_pos = PositionComponent::Center; + let y_pos = PositionComponent::Side(y_keyword, None); + Ok(Self::new(x_pos, y_pos)) + } + + /// `center center` + #[inline] + pub fn center() -> Self { + Self::new(PositionComponent::Center, PositionComponent::Center) + } +} + +impl ToCss for Position { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match (&self.horizontal, &self.vertical) { + (x_pos @ &PositionComponent::Side(_, Some(_)), &PositionComponent::Length(ref y_lop)) => { + x_pos.to_css(dest)?; + dest.write_str(" top ")?; + y_lop.to_css(dest) + }, + (&PositionComponent::Length(ref x_lop), y_pos @ &PositionComponent::Side(_, Some(_))) => { + dest.write_str("left ")?; + x_lop.to_css(dest)?; + dest.write_str(" ")?; + y_pos.to_css(dest) + }, + (x_pos, y_pos) => { + x_pos.to_css(dest)?; + dest.write_str(" ")?; + y_pos.to_css(dest) }, - _ => self.position.as_ref().map(|l| l.to_computed_value(context)) - .unwrap_or(ComputedLengthOrPercentage::Percentage(0.0)), } } } -/// The specified value of horizontal `` -pub type HorizontalPosition = GenericHorizontalPosition>; - -impl ToComputedValue for HorizontalPosition { - type ComputedValue = computed_position::HorizontalPosition; - - #[inline] - fn to_computed_value(&self, context: &Context) -> computed_position::HorizontalPosition { - GenericHorizontalPosition(self.0.computed_value(context)) - } - - #[inline] - fn from_computed_value(computed: &computed_position::HorizontalPosition) -> HorizontalPosition { - GenericHorizontalPosition(PositionValue { - keyword: None, - position: Some(ToComputedValue::from_computed_value(&computed.0)), - }) +impl HasViewportPercentage for PositionComponent { + fn has_viewport_percentage(&self) -> bool { + match *self { + PositionComponent::Length(ref lop) | + PositionComponent::Side(_, Some(ref lop)) => { + lop.has_viewport_percentage() + }, + _ => false, + } } } -impl HorizontalPosition { - #[inline] - /// Initial specified value for vertical position (`top` keyword). - pub fn left() -> HorizontalPosition { - GenericHorizontalPosition(PositionValue { - keyword: Some(Keyword::Left), - position: None, - }) +impl Parse for PositionComponent { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + Self::parse_quirky(context, input, AllowQuirks::No) } } - -/// The specified value of vertical `` -pub type VerticalPosition = GenericVerticalPosition>; - -impl ToComputedValue for VerticalPosition { - type ComputedValue = computed_position::VerticalPosition; - - #[inline] - fn to_computed_value(&self, context: &Context) -> computed_position::VerticalPosition { - GenericVerticalPosition(self.0.computed_value(context)) - } - - #[inline] - fn from_computed_value(computed: &computed_position::VerticalPosition) -> VerticalPosition { - GenericVerticalPosition(PositionValue { - keyword: None, - position: Some(ToComputedValue::from_computed_value(&computed.0)), - }) +impl PositionComponent { + /// Parses a component of a CSS position, with quirks. + pub fn parse_quirky(context: &ParserContext, + input: &mut Parser, + allow_quirks: AllowQuirks) + -> Result { + if input.try(|i| i.expect_ident_matching("center")).is_ok() { + return Ok(PositionComponent::Center); + } + if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)) { + return Ok(PositionComponent::Length(lop)); + } + let keyword = S::parse(context, input)?; + let lop = input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok(); + Ok(PositionComponent::Side(keyword, lop)) } } -impl VerticalPosition { - #[inline] - /// Initial specified value for vertical position (`top` keyword). - pub fn top() -> VerticalPosition { - GenericVerticalPosition(PositionValue { - keyword: Some(Keyword::Top), - position: None, - }) +impl PositionComponent { + /// `0%` + pub fn zero() -> Self { + PositionComponent::Length(LengthOrPercentage::Percentage(Percentage(0.))) + } +} + +impl ToCss for PositionComponent { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + PositionComponent::Center => { + dest.write_str("center") + }, + PositionComponent::Length(ref lop) => { + lop.to_css(dest) + }, + PositionComponent::Side(ref keyword, ref lop) => { + keyword.to_css(dest)?; + if let Some(ref lop) = *lop { + dest.write_str(" ")?; + lop.to_css(dest)?; + } + Ok(()) + }, + } + } +} + +impl ToComputedValue for PositionComponent { + type ComputedValue = ComputedLengthOrPercentage; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + PositionComponent::Center => { + ComputedLengthOrPercentage::Percentage(0.5) + }, + PositionComponent::Side(ref keyword, None) => { + let p = if keyword.is_start() { 0. } else { 1. }; + ComputedLengthOrPercentage::Percentage(p) + }, + PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => { + match length.to_computed_value(context) { + ComputedLengthOrPercentage::Length(length) => { + ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage { + length: -length, + percentage: Some(1.0), + }) + }, + ComputedLengthOrPercentage::Percentage(p) => { + ComputedLengthOrPercentage::Percentage(1.0 - p) + }, + ComputedLengthOrPercentage::Calc(calc) => { + ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage { + length: -calc.length, + percentage: Some(1.0 - calc.percentage.unwrap_or(0.)), + }) + }, + } + }, + PositionComponent::Side(_, Some(ref length)) | + PositionComponent::Length(ref length) => { + length.to_computed_value(context) + }, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + PositionComponent::Length(ToComputedValue::from_computed_value(computed)) + } +} + +impl PositionComponent { + /// The initial specified value of a position component, i.e. the start side. + pub fn initial_specified_value() -> Self { + PositionComponent::Side(S::start(), None) + } +} + +/// Represents a side, either horizontal or vertical, of a CSS position. +pub trait Side { + /// Returns the start side. + fn start() -> Self; + + /// Returns whether this side is the start side. + fn is_start(&self) -> bool; +} + +impl Side for X { + #[inline] + fn start() -> Self { + X::Left + } + + #[inline] + fn is_start(&self) -> bool { + *self == X::Left + } +} + +impl Side for Y { + #[inline] + fn start() -> Self { + Y::Top + } + + #[inline] + fn is_start(&self) -> bool { + *self == Y::Top } } diff --git a/tests/unit/style/parsing/basic_shape.rs b/tests/unit/style/parsing/basic_shape.rs index ead6b4bb7b0..3cef9ca1803 100644 --- a/tests/unit/style/parsing/basic_shape.rs +++ b/tests/unit/style/parsing/basic_shape.rs @@ -124,7 +124,7 @@ fn test_circle() { assert_roundtrip_basicshape!(Circle::parse, "circle(at right 5% bottom 0px)", "circle(at 95% 100%)"); assert_roundtrip_basicshape!(Circle::parse, "circle(at right 5% bottom 1px)", - "circle(at right 5% bottom 1px)"); + "circle(at left 95% bottom 1px)"); assert!(parse(Circle::parse, "circle(at 5% bottom 1px)").is_err()); assert!(parse(Circle::parse, "circle(at top 40%)").is_err()); diff --git a/tests/unit/style/parsing/mod.rs b/tests/unit/style/parsing/mod.rs index 6ba2043bd95..18d85953a58 100644 --- a/tests/unit/style/parsing/mod.rs +++ b/tests/unit/style/parsing/mod.rs @@ -20,6 +20,10 @@ fn parse Result>(f: F, s: &str) f(&context, &mut parser) } +fn parse_entirely Result>(f: F, s: &str) -> Result { + parse(|context, parser| parser.parse_entirely(|p| f(context, p)), s) +} + // This is a macro so that the file/line information // is preserved in the panic macro_rules! assert_roundtrip_with_context { diff --git a/tests/unit/style/parsing/position.rs b/tests/unit/style/parsing/position.rs index e5e8aa6379d..7a3bfe1bae3 100644 --- a/tests/unit/style/parsing/position.rs +++ b/tests/unit/style/parsing/position.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use parsing::parse; +use parsing::{parse, parse_entirely}; use style::parser::Parse; use style::values::specified::position::*; use style_traits::ToCss; @@ -28,8 +28,8 @@ fn test_position() { assert_roundtrip_with_context!(Position::parse, "right 10%", "right 10%"); // Only keywords can be reordered - assert!(parse(Position::parse, "top 40%").is_err()); - assert!(parse(Position::parse, "40% left").is_err()); + assert!(parse_entirely(Position::parse, "top 40%").is_err()); + assert!(parse_entirely(Position::parse, "40% left").is_err()); // 3 and 4 value serialization assert_roundtrip_with_context!(Position::parse, "left 10px top 15px", "left 10px top 15px"); @@ -46,31 +46,31 @@ fn test_position() { assert_roundtrip_with_context!(Position::parse, "center bottom 10px", "center bottom 10px"); // Invalid 3 value positions - assert!(parse(Position::parse, "20px 30px 20px").is_err()); - assert!(parse(Position::parse, "top 30px 20px").is_err()); - assert!(parse(Position::parse, "50% bottom 20%").is_err()); + assert!(parse_entirely(Position::parse, "20px 30px 20px").is_err()); + assert!(parse_entirely(Position::parse, "top 30px 20px").is_err()); + assert!(parse_entirely(Position::parse, "50% bottom 20%").is_err()); // Only horizontal and vertical keywords can have positions - assert!(parse(Position::parse, "center 10px left 15px").is_err()); - assert!(parse(Position::parse, "center 10px 15px").is_err()); - assert!(parse(Position::parse, "center 10px bottom").is_err()); + assert!(parse_entirely(Position::parse, "center 10px left 15px").is_err()); + assert!(parse_entirely(Position::parse, "center 10px 15px").is_err()); + assert!(parse_entirely(Position::parse, "center 10px bottom").is_err()); // "Horizontal Horizontal" or "Vertical Vertical" positions cause error - assert!(parse(Position::parse, "left right").is_err()); - assert!(parse(Position::parse, "left 10px right").is_err()); - assert!(parse(Position::parse, "left 10px right 15%").is_err()); - assert!(parse(Position::parse, "top bottom").is_err()); - assert!(parse(Position::parse, "top 10px bottom").is_err()); - assert!(parse(Position::parse, "top 10px bottom 15%").is_err()); + assert!(parse_entirely(Position::parse, "left right").is_err()); + assert!(parse_entirely(Position::parse, "left 10px right").is_err()); + assert!(parse_entirely(Position::parse, "left 10px right 15%").is_err()); + assert!(parse_entirely(Position::parse, "top bottom").is_err()); + assert!(parse_entirely(Position::parse, "top 10px bottom").is_err()); + assert!(parse_entirely(Position::parse, "top 10px bottom 15%").is_err()); - // Logical keywords are not supported in Position yet + // 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_entirely(Position::parse, "left y-start 10%").is_err()); assert!(parse(Position::parse, "x-start 20px y-end 10%").is_err()); } @@ -82,29 +82,31 @@ fn test_horizontal_position() { 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()); + assert!(parse_entirely(HorizontalPosition::parse, "20px y-end").is_err()); + assert!(parse_entirely(HorizontalPosition::parse, "20px top").is_err()); + assert!(parse_entirely(HorizontalPosition::parse, "left center").is_err()); + assert!(parse_entirely(HorizontalPosition::parse, "left top").is_err()); + assert!(parse_entirely(HorizontalPosition::parse, "left right").is_err()); + assert!(parse_entirely(HorizontalPosition::parse, "20px 30px").is_err()); + assert!(parse_entirely(HorizontalPosition::parse, "10px left").is_err()); + assert!(parse_entirely(HorizontalPosition::parse, "x-end 20%").is_err()); + assert!(parse_entirely(HorizontalPosition::parse, "20px x-start").is_err()); + + // Logical keywords are not supported in Position yet. + assert!(parse(HorizontalPosition::parse, "x-start").is_err()); + assert!(parse(HorizontalPosition::parse, "x-end").is_err()); } #[test] @@ -115,29 +117,31 @@ fn test_vertical_position() { 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, "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()); + assert!(parse_entirely(VerticalPosition::parse, "20px x-end").is_err()); + assert!(parse_entirely(VerticalPosition::parse, "20px right").is_err()); + assert!(parse_entirely(VerticalPosition::parse, "bottom top").is_err()); + assert!(parse_entirely(VerticalPosition::parse, "20px 30px").is_err()); + assert!(parse_entirely(VerticalPosition::parse, "10px top").is_err()); + assert!(parse_entirely(VerticalPosition::parse, "y-end 20%").is_err()); + assert!(parse_entirely(VerticalPosition::parse, "20px y-start").is_err()); + + // Logical keywords are not supported in Position yet. + assert!(parse(VerticalPosition::parse, "y-start").is_err()); + assert!(parse(VerticalPosition::parse, "y-end").is_err()); } #[test] diff --git a/tests/unit/style/parsing/value.rs b/tests/unit/style/parsing/value.rs index 637a6131912..2151529a293 100644 --- a/tests/unit/style/parsing/value.rs +++ b/tests/unit/style/parsing/value.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use app_units::Au; -use parsing::parse; use style::values::HasViewportPercentage; use style::values::specified::{AbsoluteLength, NoCalcLength, ViewportPercentageLength}; diff --git a/tests/unit/style/properties/serialization.rs b/tests/unit/style/properties/serialization.rs index 0e89e22cde4..f164b656ce1 100644 --- a/tests/unit/style/properties/serialization.rs +++ b/tests/unit/style/properties/serialization.rs @@ -9,8 +9,10 @@ use style::properties::longhands::outline_color::computed_value::T as ComputedCo use style::properties::parse_property_declaration_list; use style::values::{RGBA, Auto}; use style::values::CustomIdent; -use style::values::specified::{BorderStyle, BorderWidth, CSSColor, Length, NoCalcLength}; -use style::values::specified::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrAutoOrContent}; +use style::values::specified::{BorderStyle, BorderWidth, CSSColor, Length, LengthOrPercentage}; +use style::values::specified::{LengthOrPercentageOrAuto, LengthOrPercentageOrAutoOrContent}; +use style::values::specified::{NoCalcLength, PositionComponent}; +use style::values::specified::position::Y; use style::values::specified::url::SpecifiedUrl; use style_traits::ToCss; use stylesheets::block_from; @@ -796,7 +798,6 @@ mod shorthand_serialization { use style::properties::longhands::mask_position_y as position_y; use style::properties::longhands::mask_repeat as repeat; use style::properties::longhands::mask_size as size; - use style::values::generics::position::{HorizontalPosition, Keyword, PositionValue, VerticalPosition}; use style::values::specified::Image; use super::*; @@ -833,16 +834,13 @@ mod shorthand_serialization { let mode = single_vec_keyword_value!(mode, luminance); let position_x = single_vec_value_typedef!(position_x, - HorizontalPosition(PositionValue { - keyword: None, - position: Some(LengthOrPercentage::Length(NoCalcLength::from_px(7f32))), - }) + PositionComponent::Length(LengthOrPercentage::Length(NoCalcLength::from_px(7f32))) ); let position_y = single_vec_value_typedef!(position_y, - VerticalPosition(PositionValue { - keyword: Some(Keyword::Bottom), - position: Some(LengthOrPercentage::Length(NoCalcLength::from_px(4f32))), - }) + PositionComponent::Side( + Y::Bottom, + Some(LengthOrPercentage::Length(NoCalcLength::from_px(4f32))), + ) ); let size = single_vec_variant_value!(size, @@ -888,17 +886,11 @@ mod shorthand_serialization { let mode = single_vec_keyword_value!(mode, luminance); let position_x = single_vec_value_typedef!(position_x, - HorizontalPosition(PositionValue { - keyword: None, - position: Some(LengthOrPercentage::Length(NoCalcLength::from_px(7f32))), - }) + PositionComponent::Length(LengthOrPercentage::Length(NoCalcLength::from_px(7f32))) ); let position_y = single_vec_value_typedef!(position_y, - VerticalPosition(PositionValue { - keyword: None, - position: Some(LengthOrPercentage::Length(NoCalcLength::from_px(4f32))), - }) + PositionComponent::Length(LengthOrPercentage::Length(NoCalcLength::from_px(4f32))) ); let size = single_vec_variant_value!(size, diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs index e87bfeb95b8..64bbcf1874f 100644 --- a/tests/unit/style/stylesheets.rs +++ b/tests/unit/style/stylesheets.rs @@ -25,7 +25,7 @@ use style::stylearc::Arc; use style::stylesheets::{Origin, Namespaces}; use style::stylesheets::{Stylesheet, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule}; use style::values::{KeyframesName, CustomIdent}; -use style::values::specified::{LengthOrPercentageOrAuto, Percentage}; +use style::values::specified::{LengthOrPercentageOrAuto, Percentage, PositionComponent}; pub fn block_from(iterable: I) -> PropertyDeclarationBlock where I: IntoIterator { @@ -183,13 +183,11 @@ fn test_parse_stylesheet() { Importance::Normal), (PropertyDeclaration::BackgroundPositionX( longhands::background_position_x::SpecifiedValue( - vec![longhands::background_position_x::single_value - ::get_initial_position_value()])), - Importance::Normal), + vec![PositionComponent::zero()])), + Importance::Normal), (PropertyDeclaration::BackgroundPositionY( longhands::background_position_y::SpecifiedValue( - vec![longhands::background_position_y::single_value - ::get_initial_position_value()])), + vec![PositionComponent::zero()])), Importance::Normal), (PropertyDeclaration::BackgroundRepeat( longhands::background_repeat::SpecifiedValue( diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 1b33db843e9..154c948b894 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -25371,7 +25371,7 @@ "testharness" ], "mozilla/calc.html": [ - "028fc71bdc9a99d552ba552036d38fb4eef11bc1", + "47507adabc0d3642154b3ed4b1ab64d726fa682d", "testharness" ], "mozilla/canvas.initial.reset.2dstate.html": [ diff --git a/tests/wpt/mozilla/tests/mozilla/calc.html b/tests/wpt/mozilla/tests/mozilla/calc.html index 2f35b363422..d6a1a1a53cf 100644 --- a/tests/wpt/mozilla/tests/mozilla/calc.html +++ b/tests/wpt/mozilla/tests/mozilla/calc.html @@ -142,7 +142,7 @@ var otherProperties = [ ['border-width', 'calc(1px)', 'calc(1px)'], ['border-spacing', 'calc(1px)', 'calc(1px)'], ['transform-origin', 'calc(1px + 0%)', 'calc(1px + 0%) 50% 0px'], - ['perspective-origin', 'calc(1px + 0%)', 'calc(1px + 0%) 50%'], + ['perspective-origin', 'calc(1px + 0%)', 'calc(1px + 0%) center'], ['background-size', 'calc(1px + 0%)', 'calc(1px + 0%) auto'], ['background-position', 'calc(1px + 0%) calc(2px + 0%)', 'calc(1px + 0%) calc(2px + 0%)'], ['border-top-left-radius', 'calc(1px + 0%)', 'calc(1px + 0%) calc(1px + 0%)'],