Refactor BorderRadius and move it to the border modules

BorderRadius now parses itself reusing Rect<T>.
This commit is contained in:
Anthony Ramine 2017-05-28 11:24:25 +02:00
parent 4144dc74db
commit af3ede418b
20 changed files with 269 additions and 286 deletions

View file

@ -16,7 +16,6 @@ use style::logical_geometry::{LogicalMargin, WritingMode};
use style::properties::ServoComputedValues; use style::properties::ServoComputedValues;
use style::values::computed::{BorderRadiusSize, LengthOrPercentageOrAuto}; use style::values::computed::{BorderRadiusSize, LengthOrPercentageOrAuto};
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrNone}; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrNone};
use style::values::generics;
/// A collapsible margin. See CSS 2.1 § 8.3.1. /// A collapsible margin. See CSS 2.1 § 8.3.1.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -476,9 +475,8 @@ pub fn specified_border_radius(
containing_size: Size2D<Au>) containing_size: Size2D<Au>)
-> Size2D<Au> -> Size2D<Au>
{ {
let generics::BorderRadiusSize(size) = radius; let w = radius.0.width.to_used_value(containing_size.width);
let w = size.width.to_used_value(containing_size.width); let h = radius.0.height.to_used_value(containing_size.height);
let h = size.height.to_used_value(containing_size.height);
Size2D::new(w, h) Size2D::new(w, h)
} }

View file

@ -362,13 +362,14 @@ pub mod basic_shape {
use gecko_bindings::structs::StyleGeometryBox; use gecko_bindings::structs::StyleGeometryBox;
use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue}; use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
use std::borrow::Borrow; use std::borrow::Borrow;
use values::computed::{BorderRadiusSize, LengthOrPercentage}; use values::computed::basic_shape::{BasicShape, ShapeRadius};
use values::computed::basic_shape::{BasicShape, BorderRadius, ShapeRadius}; use values::computed::border::{BorderRadius, BorderRadiusSize};
use values::computed::length::LengthOrPercentage;
use values::computed::position; use values::computed::position;
use values::generics::BorderRadiusSize as GenericBorderRadiusSize;
use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon}; use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon};
use values::generics::basic_shape::{Circle, Ellipse, FillRule}; use values::generics::basic_shape::{Circle, Ellipse, FillRule};
use values::generics::basic_shape::{GeometryBox, ShapeBox}; use values::generics::basic_shape::{GeometryBox, ShapeBox};
use values::generics::border::BorderRadius as GenericBorderRadius;
use values::generics::rect::Rect; use values::generics::rect::Rect;
// using Borrow so that we can have a non-moving .into() // using Borrow so that we can have a non-moving .into()
@ -435,14 +436,14 @@ pub mod basic_shape {
fn from(other: T) -> Self { fn from(other: T) -> Self {
let other = other.borrow(); let other = other.borrow();
let get_corner = |index| { let get_corner = |index| {
GenericBorderRadiusSize::new( BorderRadiusSize::new(
LengthOrPercentage::from_gecko_style_coord(&other.data_at(index)) LengthOrPercentage::from_gecko_style_coord(&other.data_at(index))
.expect("<border-radius> should be a length, percentage, or calc value"), .expect("<border-radius> should be a length, percentage, or calc value"),
LengthOrPercentage::from_gecko_style_coord(&other.data_at(index + 1)) LengthOrPercentage::from_gecko_style_coord(&other.data_at(index + 1))
.expect("<border-radius> should be a length, percentage, or calc value")) .expect("<border-radius> should be a length, percentage, or calc value"))
}; };
BorderRadius { GenericBorderRadius {
top_left: get_corner(0), top_left: get_corner(0),
top_right: get_corner(2), top_right: get_corner(2),
bottom_right: get_corner(4), bottom_right: get_corner(4),

View file

@ -558,7 +558,7 @@ fn color_to_nscolor_zero_currentcolor(color: Color) -> structs::nscolor {
% if need_clone: % if need_clone:
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
use values::generics::BorderRadiusSize; use values::computed::border::BorderRadiusSize;
let width = GeckoStyleCoordConvertible::from_gecko_style_coord( let width = GeckoStyleCoordConvertible::from_gecko_style_coord(
&self.gecko.${gecko_ffi_name}.data_at(${x_index})) &self.gecko.${gecko_ffi_name}.data_at(${x_index}))
.expect("Failed to clone ${ident}"); .expect("Failed to clone ${ident}");
@ -3965,13 +3965,13 @@ fn static_assert() {
// the garbage data without // the garbage data without
// attempting to clean up. // attempting to clean up.
shape.mCoordinates[0].leaky_set_null(); shape.mCoordinates[0].leaky_set_null();
inset.rect.top.to_gecko_style_coord(&mut shape.mCoordinates[0]); inset.rect.0.to_gecko_style_coord(&mut shape.mCoordinates[0]);
shape.mCoordinates[1].leaky_set_null(); shape.mCoordinates[1].leaky_set_null();
inset.rect.right.to_gecko_style_coord(&mut shape.mCoordinates[1]); inset.rect.1.to_gecko_style_coord(&mut shape.mCoordinates[1]);
shape.mCoordinates[2].leaky_set_null(); shape.mCoordinates[2].leaky_set_null();
inset.rect.bottom.to_gecko_style_coord(&mut shape.mCoordinates[2]); inset.rect.2.to_gecko_style_coord(&mut shape.mCoordinates[2]);
shape.mCoordinates[3].leaky_set_null(); shape.mCoordinates[3].leaky_set_null();
inset.rect.left.to_gecko_style_coord(&mut shape.mCoordinates[3]); inset.rect.3.to_gecko_style_coord(&mut shape.mCoordinates[3]);
set_corners_from_radius(inset.round, &mut shape.mRadius); set_corners_from_radius(inset.round, &mut shape.mRadius);
} }

View file

@ -37,12 +37,13 @@ use std::fmt;
use style_traits::ToCss; use style_traits::ToCss;
use super::ComputedValues; use super::ComputedValues;
use values::CSSFloat; use values::CSSFloat;
use values::{Auto, Either, generics}; use values::{Auto, Either};
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
use values::computed::{BorderRadiusSize, ClipRect}; use values::computed::{BorderRadiusSize, ClipRect};
use values::computed::{CalcLengthOrPercentage, Context, LengthOrPercentage}; use values::computed::{CalcLengthOrPercentage, Context, LengthOrPercentage};
use values::computed::{MaxLength, MozLength}; use values::computed::{MaxLength, MozLength};
use values::computed::ToComputedValue; use values::computed::ToComputedValue;
use values::generics::border::BorderRadiusSize as GenericBorderRadiusSize;
use values::generics::position as generic_position; use values::generics::position as generic_position;
@ -877,7 +878,7 @@ impl<T: Animatable + Copy> Animatable for Point2D<T> {
impl Animatable for BorderRadiusSize { impl Animatable for BorderRadiusSize {
#[inline] #[inline]
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
self.0.add_weighted(&other.0, self_portion, other_portion).map(generics::BorderRadiusSize) self.0.add_weighted(&other.0, self_portion, other_portion).map(GenericBorderRadiusSize)
} }
#[inline] #[inline]

View file

@ -47,7 +47,7 @@ ${helpers.gecko_keyword_conversion(Keyword('border-style',
// FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage> // FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage>
% for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]: % for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
${helpers.predefined_type("border-" + corner + "-radius", "BorderRadiusSize", ${helpers.predefined_type("border-" + corner + "-radius", "BorderRadiusSize",
"computed::BorderRadiusSize::zero()", "computed::LengthOrPercentage::zero().into()",
"parse", extra_prefixes="webkit", "parse", extra_prefixes="webkit",
spec="https://drafts.csswg.org/css-backgrounds/#border-%s-radius" % corner, spec="https://drafts.csswg.org/css-backgrounds/#border-%s-radius" % corner,
boxed=True, boxed=True,

View file

@ -109,8 +109,8 @@ ${helpers.predefined_type("outline-color", "CSSColor", "computed::CSSColor::Curr
// TODO: Should they animate? // TODO: Should they animate?
% for corner in ["topleft", "topright", "bottomright", "bottomleft"]: % for corner in ["topleft", "topright", "bottomright", "bottomleft"]:
${helpers.predefined_type("-moz-outline-radius-" + corner, "BorderRadiusSize", ${helpers.predefined_type("-moz-outline-radius-" + corner, "BorderRadiusSize",
"computed::BorderRadiusSize::zero()", "computed::LengthOrPercentage::zero().into()",
"parse", products="gecko", products="gecko",
boxed=True, boxed=True,
animation_value_type="none", animation_value_type="none",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)")} spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)")}

View file

@ -205,8 +205,8 @@ pub fn parse_border(context: &ParserContext, input: &mut Parser)
'border-%s-radius' % (corner) 'border-%s-radius' % (corner)
for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left'] for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left']
)}" extra_prefixes="webkit" spec="https://drafts.csswg.org/css-backgrounds/#border-radius"> )}" extra_prefixes="webkit" spec="https://drafts.csswg.org/css-backgrounds/#border-radius">
use values::generics::serialize_radius_values; use values::generics::rect::Rect;
use values::specified::basic_shape::BorderRadius; use values::specified::border::BorderRadius;
use parser::Parse; use parser::Parse;
pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> { pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
@ -221,11 +221,17 @@ pub fn parse_border(context: &ParserContext, input: &mut Parser)
impl<'a> ToCss for LonghandsToSerialize<'a> { impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
serialize_radius_values(dest, let LonghandsToSerialize {
&self.border_top_left_radius.0, border_top_left_radius: ref tl,
&self.border_top_right_radius.0, border_top_right_radius: ref tr,
&self.border_bottom_right_radius.0, border_bottom_right_radius: ref br,
&self.border_bottom_left_radius.0) border_bottom_left_radius: ref bl,
} = *self;
let widths = Rect::new(&tl.0.width, &tr.0.width, &br.0.width, &bl.0.width);
let heights = Rect::new(&tl.0.height, &tr.0.height, &br.0.height, &bl.0.height);
BorderRadius::serialize_rects(widths, heights, dest)
} }
} }
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -67,28 +67,33 @@
'-moz-outline-radius-%s' % corner '-moz-outline-radius-%s' % corner
for corner in ['topleft', 'topright', 'bottomright', 'bottomleft'] for corner in ['topleft', 'topright', 'bottomright', 'bottomleft']
)}" products="gecko" spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)"> )}" products="gecko" spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)">
use properties::shorthands; use values::generics::rect::Rect;
use values::generics::serialize_radius_values; use values::specified::border::BorderRadius;
use parser::Parse;
pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> { pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
// Re-use border-radius parsing. let radii = try!(BorderRadius::parse(context, input));
shorthands::border_radius::parse_value(context, input).map(|longhands| { Ok(expanded! {
expanded! { _moz_outline_radius_topleft: radii.top_left,
% for corner in ["top_left", "top_right", "bottom_right", "bottom_left"]: _moz_outline_radius_topright: radii.top_right,
_moz_outline_radius_${corner.replace("_", "")}: longhands.border_${corner}_radius, _moz_outline_radius_bottomright: radii.bottom_right,
% endfor _moz_outline_radius_bottomleft: radii.bottom_left,
}
}) })
} }
impl<'a> ToCss for LonghandsToSerialize<'a> { impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
serialize_radius_values(dest, let LonghandsToSerialize {
&self._moz_outline_radius_topleft.0, _moz_outline_radius_topleft: ref tl,
&self._moz_outline_radius_topright.0, _moz_outline_radius_topright: ref tr,
&self._moz_outline_radius_bottomright.0, _moz_outline_radius_bottomright: ref br,
&self._moz_outline_radius_bottomleft.0, _moz_outline_radius_bottomleft: ref bl,
) } = *self;
let widths = Rect::new(&tl.0.width, &tr.0.width, &br.0.width, &bl.0.width);
let heights = Rect::new(&tl.0.height, &tr.0.height, &br.0.height, &bl.0.height);
BorderRadius::serialize_rects(widths, heights, dest)
} }
} }
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -10,7 +10,7 @@
use std::fmt; use std::fmt;
use style_traits::ToCss; use style_traits::ToCss;
use values::computed::LengthOrPercentage; use values::computed::LengthOrPercentage;
use values::generics::basic_shape::{BasicShape as GenericBasicShape, BorderRadius as GenericBorderRadius}; use values::generics::basic_shape::{BasicShape as GenericBasicShape};
use values::generics::basic_shape::{Circle as GenericCircle, ClippingShape as GenericClippingShape}; use values::generics::basic_shape::{Circle as GenericCircle, ClippingShape as GenericClippingShape};
use values::generics::basic_shape::{Ellipse as GenericEllipse, FloatAreaShape as GenericFloatAreaShape}; use values::generics::basic_shape::{Ellipse as GenericEllipse, FloatAreaShape as GenericFloatAreaShape};
use values::generics::basic_shape::{InsetRect as GenericInsetRect, ShapeRadius as GenericShapeRadius}; use values::generics::basic_shape::{InsetRect as GenericInsetRect, ShapeRadius as GenericShapeRadius};
@ -27,9 +27,6 @@ pub type BasicShape = GenericBasicShape<LengthOrPercentage, LengthOrPercentage,
/// The computed value of `inset()` /// The computed value of `inset()`
pub type InsetRect = GenericInsetRect<LengthOrPercentage>; pub type InsetRect = GenericInsetRect<LengthOrPercentage>;
/// The computed value of `BorderRadius`
pub type BorderRadius = GenericBorderRadius<LengthOrPercentage>;
/// A computed circle. /// A computed circle.
pub type Circle = GenericCircle<LengthOrPercentage, LengthOrPercentage, LengthOrPercentage>; pub type Circle = GenericCircle<LengthOrPercentage, LengthOrPercentage, LengthOrPercentage>;

View file

@ -8,6 +8,8 @@ use values::computed::{Number, NumberOrPercentage};
use values::computed::length::LengthOrPercentage; use values::computed::length::LengthOrPercentage;
use values::generics::border::BorderImageSlice as GenericBorderImageSlice; use values::generics::border::BorderImageSlice as GenericBorderImageSlice;
use values::generics::border::BorderImageWidthSide as GenericBorderImageWidthSide; use values::generics::border::BorderImageWidthSide as GenericBorderImageWidthSide;
use values::generics::border::BorderRadius as GenericBorderRadius;
use values::generics::border::BorderRadiusSize as GenericBorderRadiusSize;
use values::generics::rect::Rect; use values::generics::rect::Rect;
/// A computed value for the `border-image-width` property. /// A computed value for the `border-image-width` property.
@ -19,6 +21,12 @@ pub type BorderImageWidthSide = GenericBorderImageWidthSide<LengthOrPercentage,
/// A computed value for the `border-image-slice` property. /// A computed value for the `border-image-slice` property.
pub type BorderImageSlice = GenericBorderImageSlice<NumberOrPercentage>; pub type BorderImageSlice = GenericBorderImageSlice<NumberOrPercentage>;
/// A computed value for the `border-radius` property.
pub type BorderRadius = GenericBorderRadius<LengthOrPercentage>;
/// A computed value for the `border-*-radius` longhand properties.
pub type BorderRadiusSize = GenericBorderRadiusSize<LengthOrPercentage>;
impl BorderImageWidthSide { impl BorderImageWidthSide {
/// Returns `1`. /// Returns `1`.
#[inline] #[inline]

View file

@ -17,7 +17,6 @@ use std::f32::consts::PI;
use std::fmt; use std::fmt;
use style_traits::ToCss; use style_traits::ToCss;
use super::{CSSFloat, CSSInteger, RGBA}; use super::{CSSFloat, CSSInteger, RGBA};
use super::generics::BorderRadiusSize as GenericBorderRadiusSize;
use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize}; use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
use super::generics::grid::TrackList as GenericTrackList; use super::generics::grid::TrackList as GenericTrackList;
use super::specified; use super::specified;
@ -26,6 +25,7 @@ pub use app_units::Au;
pub use cssparser::Color as CSSColor; pub use cssparser::Color as CSSColor;
pub use self::background::BackgroundSize; pub use self::background::BackgroundSize;
pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageWidthSide}; pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageWidthSide};
pub use self::border::{BorderRadius, BorderRadiusSize};
pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect}; pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect};
pub use self::rect::LengthOrNumberRect; pub use self::rect::LengthOrNumberRect;
pub use super::{Auto, Either, None_}; pub use super::{Auto, Either, None_};
@ -462,20 +462,6 @@ impl ComputedValueAsSpecified for specified::AlignJustifyContent {}
impl ComputedValueAsSpecified for specified::AlignJustifySelf {} impl ComputedValueAsSpecified for specified::AlignJustifySelf {}
impl ComputedValueAsSpecified for specified::BorderStyle {} impl ComputedValueAsSpecified for specified::BorderStyle {}
/// The computed value of `BorderRadiusSize`
pub type BorderRadiusSize = GenericBorderRadiusSize<LengthOrPercentage>;
impl BorderRadiusSize {
/// Create a null value.
#[inline]
pub fn zero() -> BorderRadiusSize {
let zero = LengthOrPercentage::zero();
GenericBorderRadiusSize(Size2D::new(zero.clone(), zero))
}
}
impl Copy for BorderRadiusSize {}
#[derive(Debug, PartialEq, Clone, Copy)] #[derive(Debug, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)] #[allow(missing_docs)]

View file

@ -5,11 +5,10 @@
//! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape) //! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape)
//! types that are generic over their `ToCss` implementations. //! types that are generic over their `ToCss` implementations.
use euclid::size::Size2D;
use std::fmt; use std::fmt;
use style_traits::{HasViewportPercentage, ToCss}; use style_traits::{HasViewportPercentage, ToCss};
use values::computed::ComputedValueAsSpecified; use values::computed::ComputedValueAsSpecified;
use values::generics::BorderRadiusSize; use values::generics::border::BorderRadius;
use values::generics::position::Position; use values::generics::position::Position;
use values::generics::rect::Rect; use values::generics::rect::Rect;
use values::specified::url::SpecifiedUrl; use values::specified::url::SpecifiedUrl;
@ -71,22 +70,6 @@ pub struct InsetRect<LengthOrPercentage> {
pub round: Option<BorderRadius<LengthOrPercentage>>, pub round: Option<BorderRadius<LengthOrPercentage>>,
} }
/// A generic type used for `border-radius`, `outline-radius` and `inset()` values.
///
/// https://drafts.csswg.org/css-backgrounds-3/#border-radius
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
pub struct BorderRadius<LengthOrPercentage> {
/// The top left radius.
pub top_left: BorderRadiusSize<LengthOrPercentage>,
/// The top right radius.
pub top_right: BorderRadiusSize<LengthOrPercentage>,
/// The bottom right radius.
pub bottom_right: BorderRadiusSize<LengthOrPercentage>,
/// The bottom left radius.
pub bottom_left: BorderRadiusSize<LengthOrPercentage>,
}
/// https://drafts.csswg.org/css-shapes/#funcdef-circle /// https://drafts.csswg.org/css-shapes/#funcdef-circle
#[allow(missing_docs)] #[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -201,33 +184,6 @@ impl<L> ToCss for InsetRect<L>
} }
} }
impl<L: ToCss + PartialEq> ToCss for BorderRadius<L> {
#[inline]
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
serialize_radius_values(dest, &self.top_left.0, &self.top_right.0,
&self.bottom_right.0, &self.bottom_left.0)
}
}
/// Serialization helper for types of longhands like `border-radius` and `outline-radius`
pub fn serialize_radius_values<L, W>(dest: &mut W, top_left: &Size2D<L>,
top_right: &Size2D<L>, bottom_right: &Size2D<L>,
bottom_left: &Size2D<L>) -> fmt::Result
where L: ToCss + PartialEq, W: fmt::Write
{
Rect::new(&top_left.width, &top_right.width, &bottom_right.width, &bottom_left.width).to_css(dest)?;
if
top_left.width != top_left.height ||
top_right.width != top_right.height ||
bottom_right.width != bottom_right.height ||
bottom_left.width != bottom_left.height
{
dest.write_str(" / ")?;
Rect::new(&top_left.height, &top_right.height, &bottom_right.height, &bottom_left.height).to_css(dest)?;
}
Ok(())
}
impl<L> Default for ShapeRadius<L> { impl<L> Default for ShapeRadius<L> {
#[inline] #[inline]
fn default() -> Self { ShapeRadius::ClosestSide } fn default() -> Self { ShapeRadius::ClosestSide }

View file

@ -4,6 +4,7 @@
//! Generic types for CSS values related to borders. //! Generic types for CSS values related to borders.
use euclid::Size2D;
use std::fmt; use std::fmt;
use style_traits::ToCss; use style_traits::ToCss;
use values::generics::rect::Rect; use values::generics::rect::Rect;
@ -30,6 +31,27 @@ pub struct BorderImageSlice<NumberOrPercentage> {
pub fill: bool, pub fill: bool,
} }
/// A generic value for `border-radius`, `outline-radius` and `inset()`.
///
/// https://drafts.csswg.org/css-backgrounds-3/#border-radius
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
pub struct BorderRadius<LengthOrPercentage> {
/// The top left radius.
pub top_left: BorderRadiusSize<LengthOrPercentage>,
/// The top right radius.
pub top_right: BorderRadiusSize<LengthOrPercentage>,
/// The bottom right radius.
pub bottom_right: BorderRadiusSize<LengthOrPercentage>,
/// The bottom left radius.
pub bottom_left: BorderRadiusSize<LengthOrPercentage>,
}
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
/// A generic value for `border-*-radius` longhand properties.
pub struct BorderRadiusSize<L>(pub Size2D<L>);
impl<L, N> ToCss for BorderImageWidthSide<L, N> impl<L, N> ToCss for BorderImageWidthSide<L, N>
where L: ToCss, N: ToCss, where L: ToCss, N: ToCss,
{ {
@ -69,3 +91,81 @@ impl<N> ToCss for BorderImageSlice<N>
Ok(()) Ok(())
} }
} }
impl<L> BorderRadius<L> {
/// Returns a new `BorderRadius<L>`.
#[inline]
pub fn new(tl: BorderRadiusSize<L>,
tr: BorderRadiusSize<L>,
br: BorderRadiusSize<L>,
bl: BorderRadiusSize<L>)
-> Self {
BorderRadius {
top_left: tl,
top_right: tr,
bottom_right: br,
bottom_left: bl,
}
}
}
impl<L> BorderRadius<L>
where L: PartialEq + ToCss
{
/// Serialises two given rects following the syntax of the `border-radius``
/// property.
pub fn serialize_rects<W>(widths: Rect<&L>, heights: Rect<&L>, dest: &mut W) -> fmt::Result
where W: fmt::Write,
{
widths.to_css(dest)?;
if widths.0 != heights.0 || widths.1 != heights.1 || widths.2 != heights.2 || widths.3 != heights.3 {
dest.write_str(" / ")?;
heights.to_css(dest)?;
}
Ok(())
}
}
impl<L> ToCss for BorderRadius<L>
where L: PartialEq + ToCss
{
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let BorderRadius {
top_left: ref tl,
top_right: ref tr,
bottom_right: ref br,
bottom_left: ref bl,
} = *self;
let widths = Rect::new(&tl.0.width, &tr.0.width, &br.0.width, &bl.0.width);
let heights = Rect::new(&tl.0.height, &tr.0.height, &br.0.height, &bl.0.height);
Self::serialize_rects(widths, heights, dest)
}
}
impl<L> BorderRadiusSize<L> {
#[inline]
/// Create a new `BorderRadiusSize` for an area of given width and height.
pub fn new(width: L, height: L) -> BorderRadiusSize<L> {
BorderRadiusSize(Size2D::new(width, height))
}
}
impl<L: Clone> From<L> for BorderRadiusSize<L> {
fn from(radius: L) -> Self {
Self::new(radius.clone(), radius)
}
}
impl<L> ToCss for BorderRadiusSize<L>
where L: ToCss,
{
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where W: fmt::Write
{
self.0.width.to_css(dest)?;
dest.write_str(" ")?;
self.0.height.to_css(dest)
}
}

View file

@ -7,14 +7,11 @@
use counter_style::{Symbols, parse_counter_style_name}; use counter_style::{Symbols, parse_counter_style_name};
use cssparser::Parser; use cssparser::Parser;
use euclid::size::Size2D;
use parser::{Parse, ParserContext}; use parser::{Parse, ParserContext};
use std::fmt; use std::fmt;
use style_traits::{HasViewportPercentage, OneOrMoreCommaSeparated, ToCss}; use style_traits::{OneOrMoreCommaSeparated, ToCss};
use super::CustomIdent; use super::CustomIdent;
pub use self::basic_shape::serialize_radius_values;
pub mod background; pub mod background;
pub mod basic_shape; pub mod basic_shape;
pub mod border; pub mod border;
@ -23,47 +20,6 @@ pub mod image;
pub mod position; pub mod position;
pub mod rect; pub mod rect;
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// A type for representing CSS `width` and `height` values.
pub struct BorderRadiusSize<L>(pub Size2D<L>);
impl<L> HasViewportPercentage for BorderRadiusSize<L> {
#[inline]
fn has_viewport_percentage(&self) -> bool { false }
}
impl<L: Clone> From<L> for BorderRadiusSize<L> {
fn from(other: L) -> Self {
Self::new(other.clone(), other)
}
}
impl<L> BorderRadiusSize<L> {
#[inline]
/// Create a new `BorderRadiusSize` for an area of given width and height.
pub fn new(width: L, height: L) -> BorderRadiusSize<L> {
BorderRadiusSize(Size2D::new(width, height))
}
}
impl<L: Clone> BorderRadiusSize<L> {
#[inline]
/// Create a new `BorderRadiusSize` for a circle of given radius.
pub fn circle(radius: L) -> BorderRadiusSize<L> {
BorderRadiusSize(Size2D::new(radius.clone(), radius))
}
}
impl<L: ToCss> ToCss for BorderRadiusSize<L> {
#[inline]
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.0.width.to_css(dest)?;
dest.write_str(" ")?;
self.0.height.to_css(dest)
}
}
// https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type // https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type
define_css_keyword_enum! { SymbolsType: define_css_keyword_enum! { SymbolsType:
"cyclic" => Cyclic, "cyclic" => Cyclic,

View file

@ -12,8 +12,7 @@ use parser::{Parse, ParserContext};
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
use style_traits::ToCss; use style_traits::ToCss;
use values::generics::BorderRadiusSize; use values::generics::basic_shape::{Circle as GenericCircle};
use values::generics::basic_shape::{BorderRadius as GenericBorderRadius, Circle as GenericCircle};
use values::generics::basic_shape::{ClippingShape as GenericClippingShape, Ellipse as GenericEllipse}; use values::generics::basic_shape::{ClippingShape as GenericClippingShape, Ellipse as GenericEllipse};
use values::generics::basic_shape::{FillRule, BasicShape as GenericBasicShape}; use values::generics::basic_shape::{FillRule, BasicShape as GenericBasicShape};
use values::generics::basic_shape::{FloatAreaShape as GenericFloatAreaShape, InsetRect as GenericInsetRect}; use values::generics::basic_shape::{FloatAreaShape as GenericFloatAreaShape, InsetRect as GenericInsetRect};
@ -21,6 +20,7 @@ use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
use values::generics::basic_shape::{Polygon as GenericPolygon, ShapeRadius as GenericShapeRadius}; use values::generics::basic_shape::{Polygon as GenericPolygon, ShapeRadius as GenericShapeRadius};
use values::generics::rect::Rect; use values::generics::rect::Rect;
use values::specified::{LengthOrPercentage, Percentage}; use values::specified::{LengthOrPercentage, Percentage};
use values::specified::border::BorderRadius;
use values::specified::position::{HorizontalPosition, Position, PositionComponent, Side, VerticalPosition}; use values::specified::position::{HorizontalPosition, Position, PositionComponent, Side, VerticalPosition};
use values::specified::url::SpecifiedUrl; use values::specified::url::SpecifiedUrl;
@ -36,9 +36,6 @@ pub type BasicShape = GenericBasicShape<HorizontalPosition, VerticalPosition, Le
/// The specified value of `inset()` /// The specified value of `inset()`
pub type InsetRect = GenericInsetRect<LengthOrPercentage>; pub type InsetRect = GenericInsetRect<LengthOrPercentage>;
/// The specified value of `BorderRadius`
pub type BorderRadius = GenericBorderRadius<LengthOrPercentage>;
/// A specified circle. /// A specified circle.
pub type Circle = GenericCircle<HorizontalPosition, VerticalPosition, LengthOrPercentage>; pub type Circle = GenericCircle<HorizontalPosition, VerticalPosition, LengthOrPercentage>;
@ -140,49 +137,6 @@ impl InsetRect {
} }
} }
impl Parse for BorderRadius {
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
let mut widths = parse_one_set_of_border_values(context, input)?;
let mut heights = if input.try(|input| input.expect_delim('/')).is_ok() {
parse_one_set_of_border_values(context, input)?
} else {
[widths[0].clone(),
widths[1].clone(),
widths[2].clone(),
widths[3].clone()]
};
Ok(BorderRadius {
top_left: BorderRadiusSize::new(widths[0].take(), heights[0].take()),
top_right: BorderRadiusSize::new(widths[1].take(), heights[1].take()),
bottom_right: BorderRadiusSize::new(widths[2].take(), heights[2].take()),
bottom_left: BorderRadiusSize::new(widths[3].take(), heights[3].take()),
})
}
}
fn parse_one_set_of_border_values(context: &ParserContext, mut input: &mut Parser)
-> Result<[LengthOrPercentage; 4], ()> {
let a = try!(LengthOrPercentage::parse_non_negative(context, input));
let b = if let Ok(b) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
b
} else {
return Ok([a.clone(), a.clone(), a.clone(), a])
};
let c = if let Ok(c) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
c
} else {
return Ok([a.clone(), b.clone(), a, b])
};
if let Ok(d) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
Ok([a, b, c, d])
} else {
Ok([a, b.clone(), c, b])
}
}
impl Parse for Circle { impl Parse for Circle {
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
input.expect_function_matching("circle")?; input.expect_function_matching("circle")?;

View file

@ -8,6 +8,8 @@ use cssparser::Parser;
use parser::{Parse, ParserContext}; use parser::{Parse, ParserContext};
use values::generics::border::BorderImageSlice as GenericBorderImageSlice; use values::generics::border::BorderImageSlice as GenericBorderImageSlice;
use values::generics::border::BorderImageWidthSide as GenericBorderImageWidthSide; use values::generics::border::BorderImageWidthSide as GenericBorderImageWidthSide;
use values::generics::border::BorderRadius as GenericBorderRadius;
use values::generics::border::BorderRadiusSize as GenericBorderRadiusSize;
use values::generics::rect::Rect; use values::generics::rect::Rect;
use values::specified::{Number, NumberOrPercentage}; use values::specified::{Number, NumberOrPercentage};
use values::specified::length::LengthOrPercentage; use values::specified::length::LengthOrPercentage;
@ -21,6 +23,12 @@ pub type BorderImageWidthSide = GenericBorderImageWidthSide<LengthOrPercentage,
/// A specified value for the `border-image-slice` property. /// A specified value for the `border-image-slice` property.
pub type BorderImageSlice = GenericBorderImageSlice<NumberOrPercentage>; pub type BorderImageSlice = GenericBorderImageSlice<NumberOrPercentage>;
/// A specified value for the `border-radius` property.
pub type BorderRadius = GenericBorderRadius<LengthOrPercentage>;
/// A specified value for the `border-*-radius` longhand properties.
pub type BorderRadiusSize = GenericBorderRadiusSize<LengthOrPercentage>;
impl BorderImageWidthSide { impl BorderImageWidthSide {
/// Returns `1`. /// Returns `1`.
#[inline] #[inline]
@ -57,3 +65,31 @@ impl Parse for BorderImageSlice {
}) })
} }
} }
impl Parse for BorderRadius {
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
let widths = Rect::parse_with(context, input, LengthOrPercentage::parse_non_negative)?;
let heights = if input.try(|i| i.expect_delim('/')).is_ok() {
Rect::parse_with(context, input, LengthOrPercentage::parse_non_negative)?
} else {
widths.clone()
};
Ok(GenericBorderRadius {
top_left: BorderRadiusSize::new(widths.0, heights.0),
top_right: BorderRadiusSize::new(widths.1, heights.1),
bottom_right: BorderRadiusSize::new(widths.2, heights.2),
bottom_left: BorderRadiusSize::new(widths.3, heights.3),
})
}
}
impl Parse for BorderRadiusSize {
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
let first = LengthOrPercentage::parse_non_negative(context, input)?;
let second = input
.try(|i| LengthOrPercentage::parse_non_negative(context, i))
.unwrap_or_else(|()| first.clone());
Ok(Self::new(first, second))
}
}

View file

@ -9,7 +9,6 @@
use app_units::Au; use app_units::Au;
use context::QuirksMode; use context::QuirksMode;
use cssparser::{self, Parser, Token}; use cssparser::{self, Parser, Token};
use euclid::size::Size2D;
use itoa; use itoa;
use parser::{ParserContext, Parse}; use parser::{ParserContext, Parse};
use self::grid::TrackSizeOrRepeat; use self::grid::TrackSizeOrRepeat;
@ -23,7 +22,6 @@ use style_traits::values::specified::AllowedNumericType;
use super::{Auto, CSSFloat, CSSInteger, Either, None_}; use super::{Auto, CSSFloat, CSSInteger, Either, None_};
use super::computed::{self, Context}; use super::computed::{self, Context};
use super::computed::{Shadow as ComputedShadow, ToComputedValue}; use super::computed::{Shadow as ComputedShadow, ToComputedValue};
use super::generics::BorderRadiusSize as GenericBorderRadiusSize;
use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize}; use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
use super::generics::grid::TrackList as GenericTrackList; use super::generics::grid::TrackList as GenericTrackList;
use values::specified::calc::CalcNode; use values::specified::calc::CalcNode;
@ -32,6 +30,7 @@ use values::specified::calc::CalcNode;
pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
pub use self::background::BackgroundSize; pub use self::background::BackgroundSize;
pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageWidthSide}; pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageWidthSide};
pub use self::border::{BorderRadius, BorderRadiusSize};
pub use self::color::Color; pub use self::color::Color;
pub use self::rect::LengthOrNumberRect; pub use self::rect::LengthOrNumberRect;
pub use super::generics::grid::GridLine; pub use super::generics::grid::GridLine;
@ -281,19 +280,6 @@ pub fn parse_number_with_clamping_mode(context: &ParserContext,
} }
} }
/// The specified value of `BorderRadiusSize`
pub type BorderRadiusSize = GenericBorderRadiusSize<LengthOrPercentage>;
impl Parse for BorderRadiusSize {
#[inline]
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
let first = try!(LengthOrPercentage::parse_non_negative(context, input));
let second = input.try(|i| LengthOrPercentage::parse_non_negative(context, i))
.unwrap_or_else(|()| first.clone());
Ok(GenericBorderRadiusSize(Size2D::new(first, second)))
}
}
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)] #[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
/// An angle consisting of a value and a unit. /// An angle consisting of a value and a unit.
@ -433,21 +419,6 @@ impl Angle {
} }
} }
#[allow(missing_docs)]
pub fn parse_border_radius(context: &ParserContext, input: &mut Parser) -> Result<BorderRadiusSize, ()> {
input.try(|i| BorderRadiusSize::parse(context, i)).or_else(|_| {
match_ignore_ascii_case! { &try!(input.expect_ident()),
"thin" => Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(NoCalcLength::from_px(1.)))),
"medium" => Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(NoCalcLength::from_px(3.)))),
"thick" => Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(NoCalcLength::from_px(5.)))),
_ => Err(())
}
})
}
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn parse_border_width(context: &ParserContext, input: &mut Parser) -> Result<Length, ()> { pub fn parse_border_width(context: &ParserContext, input: &mut Parser) -> Result<Length, ()> {
input.try(|i| Length::parse_non_negative(context, i)).or_else(|()| { input.try(|i| Length::parse_non_negative(context, i)).or_else(|()| {

View file

@ -56,6 +56,13 @@ impl<T: HasViewportPercentage> HasViewportPercentage for Option<T> {
} }
} }
impl<T: HasViewportPercentage, U> HasViewportPercentage for TypedSize2D<T, U> {
#[inline]
fn has_viewport_percentage(&self) -> bool {
self.width.has_viewport_percentage() || self.height.has_viewport_percentage()
}
}
impl<T: HasViewportPercentage> HasViewportPercentage for Vec<T> { impl<T: HasViewportPercentage> HasViewportPercentage for Vec<T> {
#[inline] #[inline]
fn has_viewport_percentage(&self) -> bool { fn has_viewport_percentage(&self) -> bool {

View file

@ -19,23 +19,6 @@ macro_rules! assert_roundtrip_basicshape {
}; };
} }
macro_rules! assert_border_radius_values {
($input:expr; $tlw:expr, $trw:expr, $brw:expr, $blw:expr ;
$tlh:expr, $trh:expr, $brh:expr, $blh:expr) => {
let input = parse(BorderRadius::parse, $input)
.expect(&format!("Failed parsing {} as border radius",
$input));
assert_eq!(::style_traits::ToCss::to_css_string(&input.top_left.0.width), $tlw);
assert_eq!(::style_traits::ToCss::to_css_string(&input.top_right.0.width), $trw);
assert_eq!(::style_traits::ToCss::to_css_string(&input.bottom_right.0.width), $brw);
assert_eq!(::style_traits::ToCss::to_css_string(&input.bottom_left.0.width), $blw);
assert_eq!(::style_traits::ToCss::to_css_string(&input.top_left.0.height), $tlh);
assert_eq!(::style_traits::ToCss::to_css_string(&input.top_right.0.height), $trh);
assert_eq!(::style_traits::ToCss::to_css_string(&input.bottom_right.0.height), $brh);
assert_eq!(::style_traits::ToCss::to_css_string(&input.bottom_left.0.height), $blh);
}
}
#[test] #[test]
fn test_inset() { fn test_inset() {
assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px)"); assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px)");
@ -46,47 +29,6 @@ fn test_inset() {
assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px round 10px 20px 30px 40px / 1px 2px 3px 4px)"); assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px round 10px 20px 30px 40px / 1px 2px 3px 4px)");
} }
#[test]
fn test_border_radius() {
assert_border_radius_values!("10px";
"10px", "10px", "10px", "10px" ;
"10px", "10px", "10px", "10px");
assert_border_radius_values!("10px 20px";
"10px", "20px", "10px", "20px" ;
"10px", "20px", "10px", "20px");
assert_border_radius_values!("10px 20px 30px";
"10px", "20px", "30px", "20px" ;
"10px", "20px", "30px", "20px");
assert_border_radius_values!("10px 20px 30px 40px";
"10px", "20px", "30px", "40px" ;
"10px", "20px", "30px", "40px");
assert_border_radius_values!("10% / 20px";
"10%", "10%", "10%", "10%" ;
"20px", "20px", "20px", "20px");
assert_border_radius_values!("10px / 20px 30px";
"10px", "10px", "10px", "10px" ;
"20px", "30px", "20px", "30px");
assert_border_radius_values!("10px 20px 30px 40px / 1px 2px 3px 4px";
"10px", "20px", "30px", "40px" ;
"1px", "2px", "3px", "4px");
assert_border_radius_values!("10px 20px 30px 40px / 1px 2px 3px 4px";
"10px", "20px", "30px", "40px" ;
"1px", "2px", "3px", "4px");
assert_border_radius_values!("10px 20px 30px 40px / 1px 2px 3px 4px";
"10px", "20px", "30px", "40px" ;
"1px", "2px", "3px", "4px");
assert_border_radius_values!("10px -20px 30px 40px";
"10px", "10px", "10px", "10px";
"10px", "10px", "10px", "10px");
assert_border_radius_values!("10px 20px -30px 40px";
"10px", "20px", "10px", "20px";
"10px", "20px", "10px", "20px");
assert_border_radius_values!("10px 20px 30px -40px";
"10px", "20px", "30px", "20px";
"10px", "20px", "30px", "20px");
assert!(parse(BorderRadius::parse, "-10px 20px 30px 40px").is_err());
}
#[test] #[test]
fn test_circle() { fn test_circle() {
assert_roundtrip_basicshape!(Circle::parse, "circle(at center)", "circle(at 50% 50%)"); assert_roundtrip_basicshape!(Circle::parse, "circle(at center)", "circle(at 50% 50%)");

View file

@ -8,6 +8,7 @@ use style::properties::MaybeBoxed;
use style::properties::longhands::{border_image_outset, border_image_repeat, border_image_slice}; use style::properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
use style::properties::longhands::{border_image_source, border_image_width}; use style::properties::longhands::{border_image_source, border_image_width};
use style::properties::shorthands::border_image; use style::properties::shorthands::border_image;
use style::values::specified::BorderRadius;
use style_traits::ToCss; use style_traits::ToCss;
macro_rules! assert_longhand { macro_rules! assert_longhand {
@ -22,6 +23,64 @@ macro_rules! assert_initial {
} }
} }
macro_rules! assert_border_radius_values {
($input:expr; $tlw:expr, $trw:expr, $brw:expr, $blw:expr ;
$tlh:expr, $trh:expr, $brh:expr, $blh:expr) => {
let input = parse(BorderRadius::parse, $input)
.expect(&format!("Failed parsing {} as border radius",
$input));
assert_eq!(::style_traits::ToCss::to_css_string(&input.top_left.0.width), $tlw);
assert_eq!(::style_traits::ToCss::to_css_string(&input.top_right.0.width), $trw);
assert_eq!(::style_traits::ToCss::to_css_string(&input.bottom_right.0.width), $brw);
assert_eq!(::style_traits::ToCss::to_css_string(&input.bottom_left.0.width), $blw);
assert_eq!(::style_traits::ToCss::to_css_string(&input.top_left.0.height), $tlh);
assert_eq!(::style_traits::ToCss::to_css_string(&input.top_right.0.height), $trh);
assert_eq!(::style_traits::ToCss::to_css_string(&input.bottom_right.0.height), $brh);
assert_eq!(::style_traits::ToCss::to_css_string(&input.bottom_left.0.height), $blh);
}
}
#[test]
fn test_border_radius() {
assert_border_radius_values!("10px";
"10px", "10px", "10px", "10px" ;
"10px", "10px", "10px", "10px");
assert_border_radius_values!("10px 20px";
"10px", "20px", "10px", "20px" ;
"10px", "20px", "10px", "20px");
assert_border_radius_values!("10px 20px 30px";
"10px", "20px", "30px", "20px" ;
"10px", "20px", "30px", "20px");
assert_border_radius_values!("10px 20px 30px 40px";
"10px", "20px", "30px", "40px" ;
"10px", "20px", "30px", "40px");
assert_border_radius_values!("10% / 20px";
"10%", "10%", "10%", "10%" ;
"20px", "20px", "20px", "20px");
assert_border_radius_values!("10px / 20px 30px";
"10px", "10px", "10px", "10px" ;
"20px", "30px", "20px", "30px");
assert_border_radius_values!("10px 20px 30px 40px / 1px 2px 3px 4px";
"10px", "20px", "30px", "40px" ;
"1px", "2px", "3px", "4px");
assert_border_radius_values!("10px 20px 30px 40px / 1px 2px 3px 4px";
"10px", "20px", "30px", "40px" ;
"1px", "2px", "3px", "4px");
assert_border_radius_values!("10px 20px 30px 40px / 1px 2px 3px 4px";
"10px", "20px", "30px", "40px" ;
"1px", "2px", "3px", "4px");
assert_border_radius_values!("10px -20px 30px 40px";
"10px", "10px", "10px", "10px";
"10px", "10px", "10px", "10px");
assert_border_radius_values!("10px 20px -30px 40px";
"10px", "20px", "10px", "20px";
"10px", "20px", "10px", "20px");
assert_border_radius_values!("10px 20px 30px -40px";
"10px", "20px", "30px", "20px";
"10px", "20px", "30px", "20px");
assert!(parse(BorderRadius::parse, "-10px 20px 30px 40px").is_err());
}
#[test] #[test]
fn border_image_shorthand_should_parse_when_all_properties_specified() { fn border_image_shorthand_should_parse_when_all_properties_specified() {
let input = "linear-gradient(red, blue) 30 30% 45 fill / 20px 40px / 10px round stretch"; let input = "linear-gradient(red, blue) 30 30% 45 fill / 20px 40px / 10px round stretch";