Refactor Position

A specified position is now a struct made of two values of different types,
the first one being PositionComponent<X>, and the second one PositionComponent<Y>.

A position component is represented by the new enum PositionComponent<Side>,
with the three values Center, Length(LengthOrPercentage), and
Side(Side, Option<LengthOrPercentage>).

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 <position> values followed by arbitrary tokens,
and where it would fail to prefer "left" to "right" when serialising positions
in basic shapes.
This commit is contained in:
Anthony Ramine 2017-05-08 03:09:26 +02:00
parent 0040160b38
commit 70ec61cf01
22 changed files with 484 additions and 887 deletions

View file

@ -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());

View file

@ -20,6 +20,10 @@ fn parse<T, F: Fn(&ParserContext, &mut Parser) -> Result<T, ()>>(f: F, s: &str)
f(&context, &mut parser)
}
fn parse_entirely<T, F: Fn(&ParserContext, &mut Parser) -> Result<T, ()>>(f: F, s: &str) -> Result<T, ()> {
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 {

View file

@ -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]

View file

@ -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};

View file

@ -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,

View file

@ -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<I>(iterable: I) -> PropertyDeclarationBlock
where I: IntoIterator<Item=(PropertyDeclaration, Importance)> {
@ -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(

View file

@ -25371,7 +25371,7 @@
"testharness"
],
"mozilla/calc.html": [
"028fc71bdc9a99d552ba552036d38fb4eef11bc1",
"47507adabc0d3642154b3ed4b1ab64d726fa682d",
"testharness"
],
"mozilla/canvas.initial.reset.2dstate.html": [

View file

@ -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%)'],