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

@ -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)"></%call>
% 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() {
}
</%self:simple_image_array_property>
% 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

View file

@ -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<f32, LengthOrPercentage> {}
impl<T: RepeatableListAnimatable> Animatable for SmallVec<[T; 1]> {
@ -1336,46 +1336,6 @@ impl<H: Animatable, V: Animatable> Animatable for generic_position::Position<H,
impl<H, V> RepeatableListAnimatable for generic_position::Position<H, V>
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, ()> {
self.0.interpolate(&other.0, progress).map(generic_position::HorizontalPosition)
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_distance(&other.0)
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
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, ()> {
self.0.interpolate(&other.0, progress).map(generic_position::VerticalPosition)
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_distance(&other.0)
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_squared_distance(&other.0)
}
}
impl RepeatableListAnimatable for VerticalPosition {}
/// https://drafts.csswg.org/css-transitions/#animtype-rect
impl Animatable for ClipRect {
#[inline]

View file

@ -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>
<%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))),
})
}
</%helpers:predefined_type>
% 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">

View file

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

View file

@ -90,49 +90,14 @@ ${helpers.single_keyword("mask-mode",
}
</%helpers:vector_longhand>
<%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<Self, ()> {
Ok(MaskPositionX(try!(self.0.interpolate(&other.0, progress))))
}
}
impl RepeatableListAnimatable for MaskPositionX {}
</%helpers:vector_longhand>
<%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<Self, ()> {
Ok(MaskPositionY(try!(self.0.interpolate(&other.0, progress))))
}
}
impl RepeatableListAnimatable for MaskPositionY {}
</%helpers:vector_longhand>
% 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",

View file

@ -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<background_origin::single_value::SpecifiedValue> 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(());
}

View file

@ -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<mask_origin::single_value::SpecifiedValue> 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(());
}