зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #16994 - Refactor basic shapes and fix a silly bug (from servo:derive-all-the-things); r=emilio
Source-Repo: https://github.com/servo/servo Source-Revision: 9a5e6fb553b76e6fbd0a532723cb34c136ed9fec --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : c54c9f7baab0ea82c381930ce7bf4a0ca6d1219d
This commit is contained in:
Родитель
ca4aa9a838
Коммит
8823abc875
|
@ -363,10 +363,12 @@ pub mod basic_shape {
|
|||
use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
|
||||
use std::borrow::Borrow;
|
||||
use values::computed::{BorderRadiusSize, LengthOrPercentage};
|
||||
use values::computed::basic_shape::*;
|
||||
use values::computed::basic_shape::{BasicShape, BorderRadius, ShapeRadius};
|
||||
use values::computed::position;
|
||||
use values::generics::BorderRadiusSize as GenericBorderRadiusSize;
|
||||
use values::generics::basic_shape::FillRule;
|
||||
use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon};
|
||||
use values::generics::basic_shape::{Circle, Ellipse, FillRule};
|
||||
use values::generics::basic_shape::{GeometryBox, ShapeBox};
|
||||
|
||||
// using Borrow so that we can have a non-moving .into()
|
||||
impl<T: Borrow<StyleBasicShape>> From<T> for BasicShape {
|
||||
|
@ -379,7 +381,7 @@ pub mod basic_shape {
|
|||
let b = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[2]);
|
||||
let l = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[3]);
|
||||
let round = (&other.mRadius).into();
|
||||
BasicShape::Inset(InsetRect {
|
||||
GenericBasicShape::Inset(InsetRect {
|
||||
top: t.expect("inset() offset should be a length, percentage, or calc value"),
|
||||
right: r.expect("inset() offset should be a length, percentage, or calc value"),
|
||||
bottom: b.expect("inset() offset should be a length, percentage, or calc value"),
|
||||
|
@ -388,13 +390,13 @@ pub mod basic_shape {
|
|||
})
|
||||
}
|
||||
StyleBasicShapeType::Circle => {
|
||||
BasicShape::Circle(Circle {
|
||||
GenericBasicShape::Circle(Circle {
|
||||
radius: (&other.mCoordinates[0]).into(),
|
||||
position: (&other.mPosition).into()
|
||||
})
|
||||
}
|
||||
StyleBasicShapeType::Ellipse => {
|
||||
BasicShape::Ellipse(Ellipse {
|
||||
GenericBasicShape::Ellipse(Ellipse {
|
||||
semiaxis_x: (&other.mCoordinates[0]).into(),
|
||||
semiaxis_y: (&other.mCoordinates[1]).into(),
|
||||
position: (&other.mPosition).into()
|
||||
|
@ -416,7 +418,7 @@ pub mod basic_shape {
|
|||
.expect("polygon() coordinate should be a length, percentage, or calc value")
|
||||
))
|
||||
}
|
||||
BasicShape::Polygon(Polygon {
|
||||
GenericBasicShape::Polygon(Polygon {
|
||||
fill: fill_rule,
|
||||
coordinates: coords,
|
||||
})
|
||||
|
|
|
@ -3853,8 +3853,7 @@ fn static_assert() {
|
|||
use gecko_bindings::structs::{StyleFillRule, StyleGeometryBox, StyleShapeSource};
|
||||
use gecko::conversions::basic_shape::set_corners_from_radius;
|
||||
use gecko::values::GeckoStyleCoordConvertible;
|
||||
use values::computed::basic_shape::BasicShape;
|
||||
use values::generics::basic_shape::{ShapeSource, FillRule};
|
||||
use values::generics::basic_shape::{BasicShape, FillRule, ShapeSource};
|
||||
let ref mut ${ident} = self.gecko.${gecko_ffi_name};
|
||||
// clean up existing struct
|
||||
unsafe { Gecko_DestroyShapeSource(${ident}) };
|
||||
|
|
|
@ -2432,7 +2432,7 @@ ${helpers.single_keyword("-moz-orient",
|
|||
}
|
||||
</%helpers:longhand>
|
||||
|
||||
${helpers.predefined_type("shape-outside", "basic_shape::ShapeWithShapeBox",
|
||||
${helpers.predefined_type("shape-outside", "basic_shape::FloatAreaShape",
|
||||
"generics::basic_shape::ShapeSource::None",
|
||||
products="gecko", boxed="True",
|
||||
animation_value_type="none",
|
||||
|
|
|
@ -58,7 +58,7 @@ ${helpers.single_keyword("mask-type", "luminance alpha",
|
|||
products="gecko", animation_value_type="none",
|
||||
spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type")}
|
||||
|
||||
${helpers.predefined_type("clip-path", "basic_shape::ShapeWithGeometryBox",
|
||||
${helpers.predefined_type("clip-path", "basic_shape::ClippingShape",
|
||||
"generics::basic_shape::ShapeSource::None",
|
||||
products="gecko", boxed="True",
|
||||
animation_value_type="none", flags="CREATES_STACKING_CONTEXT",
|
||||
|
|
|
@ -10,90 +10,55 @@
|
|||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use values::computed::LengthOrPercentage;
|
||||
use values::computed::position::Position;
|
||||
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::generics::basic_shape::{BasicShape as GenericBasicShape, BorderRadius as GenericBorderRadius};
|
||||
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::{InsetRect as GenericInsetRect, ShapeRadius as GenericShapeRadius};
|
||||
|
||||
pub use values::generics::basic_shape::FillRule;
|
||||
pub use values::specified::basic_shape::{self, GeometryBox, ShapeBox};
|
||||
/// A specified clipping shape.
|
||||
pub type ClippingShape = GenericClippingShape<BasicShape>;
|
||||
|
||||
/// The computed value used by `clip-path`
|
||||
pub type ShapeWithGeometryBox = ShapeSource<BasicShape, GeometryBox>;
|
||||
/// A specified float area shape.
|
||||
pub type FloatAreaShape = GenericFloatAreaShape<BasicShape>;
|
||||
|
||||
/// The computed value used by `shape-outside`
|
||||
pub type ShapeWithShapeBox = ShapeSource<BasicShape, ShapeBox>;
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum BasicShape {
|
||||
Inset(InsetRect),
|
||||
Circle(Circle),
|
||||
Ellipse(Ellipse),
|
||||
Polygon(Polygon),
|
||||
}
|
||||
|
||||
impl ToCss for BasicShape {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
BasicShape::Inset(ref rect) => rect.to_css(dest),
|
||||
BasicShape::Circle(ref circle) => circle.to_css(dest),
|
||||
BasicShape::Ellipse(ref e) => e.to_css(dest),
|
||||
BasicShape::Polygon(ref poly) => poly.to_css(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
/// A computed basic shape.
|
||||
pub type BasicShape = GenericBasicShape<LengthOrPercentage, LengthOrPercentage, LengthOrPercentage>;
|
||||
|
||||
/// The computed value of `inset()`
|
||||
pub type InsetRect = GenericInsetRect<LengthOrPercentage>;
|
||||
|
||||
#[derive(Clone, PartialEq, Copy, Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Circle {
|
||||
pub radius: ShapeRadius,
|
||||
pub position: Position,
|
||||
}
|
||||
|
||||
impl ToCss for Circle {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
try!(self.radius.to_css(dest));
|
||||
try!(dest.write_str(" at "));
|
||||
self.position.to_css(dest)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Copy, Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Ellipse {
|
||||
pub semiaxis_x: ShapeRadius,
|
||||
pub semiaxis_y: ShapeRadius,
|
||||
pub position: Position,
|
||||
}
|
||||
|
||||
impl ToCss for Ellipse {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
try!(dest.write_str("ellipse("));
|
||||
if (self.semiaxis_x, self.semiaxis_y) != Default::default() {
|
||||
try!(self.semiaxis_x.to_css(dest));
|
||||
try!(dest.write_str(" "));
|
||||
try!(self.semiaxis_y.to_css(dest));
|
||||
try!(dest.write_str(" "));
|
||||
}
|
||||
try!(dest.write_str("at "));
|
||||
try!(self.position.to_css(dest));
|
||||
dest.write_str(")")
|
||||
}
|
||||
}
|
||||
|
||||
/// The computed value of `Polygon`
|
||||
pub type Polygon = GenericPolygon<LengthOrPercentage>;
|
||||
|
||||
/// The computed value of `BorderRadius`
|
||||
pub type BorderRadius = GenericBorderRadius<LengthOrPercentage>;
|
||||
|
||||
/// A computed circle.
|
||||
pub type Circle = GenericCircle<LengthOrPercentage, LengthOrPercentage, LengthOrPercentage>;
|
||||
|
||||
/// A computed ellipse.
|
||||
pub type Ellipse = GenericEllipse<LengthOrPercentage, LengthOrPercentage, LengthOrPercentage>;
|
||||
|
||||
/// The computed value of `ShapeRadius`
|
||||
pub type ShapeRadius = GenericShapeRadius<LengthOrPercentage>;
|
||||
|
||||
impl Copy for ShapeRadius {}
|
||||
impl ToCss for Circle {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
dest.write_str("circle(")?;
|
||||
self.radius.to_css(dest)?;
|
||||
dest.write_str(" at ")?;
|
||||
self.position.to_css(dest)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for Ellipse {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
dest.write_str("ellipse(")?;
|
||||
if (self.semiaxis_x, self.semiaxis_y) != Default::default() {
|
||||
self.semiaxis_x.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.semiaxis_y.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
dest.write_str("at ")?;
|
||||
self.position.to_css(dest)?;
|
||||
dest.write_str(")")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,31 +5,217 @@
|
|||
//! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape)
|
||||
//! types that are generic over their `ToCss` implementations.
|
||||
|
||||
use cssparser::Parser;
|
||||
use euclid::size::Size2D;
|
||||
use parser::{Parse, ParserContext};
|
||||
use properties::shorthands::serialize_four_sides;
|
||||
use std::ascii::AsciiExt;
|
||||
use std::fmt;
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use values::computed::ComputedValueAsSpecified;
|
||||
use values::generics::BorderRadiusSize;
|
||||
use values::generics::position::Position;
|
||||
use values::specified::url::SpecifiedUrl;
|
||||
|
||||
/// A clipping shape, for `clip-path`.
|
||||
pub type ClippingShape<BasicShape> = ShapeSource<BasicShape, GeometryBox>;
|
||||
|
||||
/// https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box
|
||||
#[allow(missing_docs)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum GeometryBox {
|
||||
FillBox,
|
||||
StrokeBox,
|
||||
ViewBox,
|
||||
ShapeBox(ShapeBox),
|
||||
}
|
||||
impl ComputedValueAsSpecified for GeometryBox {}
|
||||
|
||||
/// A float area shape, for `shape-outside`.
|
||||
pub type FloatAreaShape<BasicShape> = ShapeSource<BasicShape, ShapeBox>;
|
||||
|
||||
// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
|
||||
define_css_keyword_enum!(ShapeBox:
|
||||
"margin-box" => MarginBox,
|
||||
"border-box" => BorderBox,
|
||||
"padding-box" => PaddingBox,
|
||||
"content-box" => ContentBox
|
||||
);
|
||||
add_impls_for_keyword_enum!(ShapeBox);
|
||||
|
||||
/// A shape source, for some reference box.
|
||||
#[allow(missing_docs)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
pub enum ShapeSource<BasicShape, ReferenceBox> {
|
||||
Url(SpecifiedUrl),
|
||||
Shape(BasicShape, Option<ReferenceBox>),
|
||||
Box(ReferenceBox),
|
||||
None,
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
pub enum BasicShape<H, V, LengthOrPercentage> {
|
||||
Inset(InsetRect<LengthOrPercentage>),
|
||||
Circle(Circle<H, V, LengthOrPercentage>),
|
||||
Ellipse(Ellipse<H, V, LengthOrPercentage>),
|
||||
Polygon(Polygon<LengthOrPercentage>),
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-shapes/#funcdef-inset
|
||||
#[allow(missing_docs)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
pub struct InsetRect<LengthOrPercentage> {
|
||||
pub top: LengthOrPercentage,
|
||||
pub right: LengthOrPercentage,
|
||||
pub bottom: LengthOrPercentage,
|
||||
pub left: 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
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct BorderRadius<L> {
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
pub struct BorderRadius<LengthOrPercentage> {
|
||||
/// The top left radius.
|
||||
pub top_left: BorderRadiusSize<L>,
|
||||
pub top_left: BorderRadiusSize<LengthOrPercentage>,
|
||||
/// The top right radius.
|
||||
pub top_right: BorderRadiusSize<L>,
|
||||
pub top_right: BorderRadiusSize<LengthOrPercentage>,
|
||||
/// The bottom right radius.
|
||||
pub bottom_right: BorderRadiusSize<L>,
|
||||
pub bottom_right: BorderRadiusSize<LengthOrPercentage>,
|
||||
/// The bottom left radius.
|
||||
pub bottom_left: BorderRadiusSize<L>,
|
||||
pub bottom_left: BorderRadiusSize<LengthOrPercentage>,
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-shapes/#funcdef-circle
|
||||
#[allow(missing_docs)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
|
||||
pub struct Circle<H, V, LengthOrPercentage> {
|
||||
pub position: Position<H, V>,
|
||||
pub radius: ShapeRadius<LengthOrPercentage>,
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-shapes/#funcdef-ellipse
|
||||
#[allow(missing_docs)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
|
||||
pub struct Ellipse<H, V, LengthOrPercentage> {
|
||||
pub position: Position<H, V>,
|
||||
pub semiaxis_x: ShapeRadius<LengthOrPercentage>,
|
||||
pub semiaxis_y: ShapeRadius<LengthOrPercentage>,
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-shapes/#typedef-shape-radius
|
||||
#[allow(missing_docs)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
|
||||
pub enum ShapeRadius<LengthOrPercentage> {
|
||||
Length(LengthOrPercentage),
|
||||
ClosestSide,
|
||||
FarthestSide,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
/// A generic type for representing the `polygon()` function
|
||||
///
|
||||
/// https://drafts.csswg.org/css-shapes/#funcdef-polygon
|
||||
pub struct Polygon<LengthOrPercentage> {
|
||||
/// The filling rule for a polygon.
|
||||
pub fill: FillRule,
|
||||
/// A collection of (x, y) coordinates to draw the polygon.
|
||||
pub coordinates: Vec<(LengthOrPercentage, LengthOrPercentage)>,
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-shapes/#typedef-fill-rule
|
||||
// NOTE: Basic shapes spec says that these are the only two values, however
|
||||
// https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
|
||||
// says that it can also be `inherit`
|
||||
define_css_keyword_enum!(FillRule:
|
||||
"nonzero" => NonZero,
|
||||
"evenodd" => EvenOdd
|
||||
);
|
||||
add_impls_for_keyword_enum!(FillRule);
|
||||
|
||||
impl<B, T> HasViewportPercentage for ShapeSource<B, T> {
|
||||
#[inline]
|
||||
fn has_viewport_percentage(&self) -> bool { false }
|
||||
}
|
||||
|
||||
impl<B: ToCss, T: ToCss> ToCss for ShapeSource<B, T> {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
ShapeSource::Url(ref url) => url.to_css(dest),
|
||||
ShapeSource::Shape(ref shape, Some(ref ref_box)) => {
|
||||
shape.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
ref_box.to_css(dest)
|
||||
},
|
||||
ShapeSource::Shape(ref shape, None) => shape.to_css(dest),
|
||||
ShapeSource::Box(ref val) => val.to_css(dest),
|
||||
ShapeSource::None => dest.write_str("none"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for GeometryBox {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
GeometryBox::FillBox => dest.write_str("fill-box"),
|
||||
GeometryBox::StrokeBox => dest.write_str("stroke-box"),
|
||||
GeometryBox::ViewBox => dest.write_str("view-box"),
|
||||
GeometryBox::ShapeBox(s) => s.to_css(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, V, L> ToCss for BasicShape<H, V, L>
|
||||
where H: ToCss,
|
||||
V: ToCss,
|
||||
L: PartialEq + ToCss,
|
||||
Circle<H, V, L>: ToCss,
|
||||
Ellipse<H, V, L>: ToCss,
|
||||
{
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
BasicShape::Inset(ref rect) => rect.to_css(dest),
|
||||
BasicShape::Circle(ref circle) => circle.to_css(dest),
|
||||
BasicShape::Ellipse(ref ellipse) => ellipse.to_css(dest),
|
||||
BasicShape::Polygon(ref polygon) => polygon.to_css(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: ToCss + PartialEq> ToCss for InsetRect<L> {
|
||||
// XXXManishearth We should try to reduce the number of values printed here
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
dest.write_str("inset(")?;
|
||||
self.top.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.right.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.bottom.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.left.to_css(dest)?;
|
||||
if let Some(ref radius) = self.round {
|
||||
dest.write_str(" round ")?;
|
||||
radius.to_css(dest)?;
|
||||
}
|
||||
|
||||
dest.write_str(")")
|
||||
}
|
||||
}
|
||||
|
||||
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`
|
||||
|
@ -51,24 +237,6 @@ pub fn serialize_radius_values<L, W>(dest: &mut W, top_left: &Size2D<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)
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-shapes/#typedef-shape-radius
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ShapeRadius<L> {
|
||||
Length(L),
|
||||
ClosestSide,
|
||||
FarthestSide,
|
||||
}
|
||||
|
||||
impl<L> Default for ShapeRadius<L> {
|
||||
#[inline]
|
||||
fn default() -> Self { ShapeRadius::ClosestSide }
|
||||
|
@ -85,64 +253,6 @@ impl<L: ToCss> ToCss for ShapeRadius<L> {
|
|||
}
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-shapes/#typedef-fill-rule
|
||||
// NOTE: Basic shapes spec says that these are the only two values, however
|
||||
// https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
|
||||
// says that it can also be `inherit`
|
||||
define_css_keyword_enum!(FillRule:
|
||||
"nonzero" => NonZero,
|
||||
"evenodd" => EvenOdd
|
||||
);
|
||||
|
||||
impl ComputedValueAsSpecified for FillRule {}
|
||||
|
||||
impl Default for FillRule {
|
||||
#[inline]
|
||||
fn default() -> Self { FillRule::NonZero }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
/// A generic type for representing the `polygon()` function
|
||||
///
|
||||
/// https://drafts.csswg.org/css-shapes/#funcdef-polygon
|
||||
pub struct Polygon<L> {
|
||||
/// The filling rule for a polygon.
|
||||
pub fill: FillRule,
|
||||
/// A collection of (x, y) coordinates to draw the polygon.
|
||||
pub coordinates: Vec<(L, L)>,
|
||||
}
|
||||
|
||||
impl<L: Parse> Polygon<L> {
|
||||
/// Parse the inner arguments of a `polygon` function.
|
||||
pub fn parse_function_arguments(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
let fill = input.try(|i| -> Result<_, ()> {
|
||||
let fill = FillRule::parse(i)?;
|
||||
i.expect_comma()?; // only eat the comma if there is something before it
|
||||
Ok(fill)
|
||||
}).ok().unwrap_or_default();
|
||||
|
||||
let buf = input.parse_comma_separated(|i| {
|
||||
Ok((L::parse(context, i)?, L::parse(context, i)?))
|
||||
})?;
|
||||
|
||||
Ok(Polygon {
|
||||
fill: fill,
|
||||
coordinates: buf,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: Parse> Parse for Polygon<L> {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
match input.expect_function() {
|
||||
Ok(ref s) if s.eq_ignore_ascii_case("polygon") =>
|
||||
input.parse_nested_block(|i| Polygon::parse_function_arguments(context, i)),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: ToCss> ToCss for Polygon<L> {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
dest.write_str("polygon(")?;
|
||||
|
@ -165,105 +275,7 @@ impl<L: ToCss> ToCss for Polygon<L> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
/// https://drafts.csswg.org/css-shapes/#funcdef-inset
|
||||
#[allow(missing_docs)]
|
||||
pub struct InsetRect<L> {
|
||||
pub top: L,
|
||||
pub right: L,
|
||||
pub bottom: L,
|
||||
pub left: L,
|
||||
pub round: Option<BorderRadius<L>>,
|
||||
}
|
||||
|
||||
impl<L: ToCss + PartialEq> ToCss for InsetRect<L> {
|
||||
// XXXManishearth We should try to reduce the number of values printed here
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
dest.write_str("inset(")?;
|
||||
self.top.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.right.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.bottom.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.left.to_css(dest)?;
|
||||
if let Some(ref radius) = self.round {
|
||||
dest.write_str(" round ")?;
|
||||
radius.to_css(dest)?;
|
||||
}
|
||||
|
||||
dest.write_str(")")
|
||||
}
|
||||
}
|
||||
|
||||
/// A shape source, for some reference box
|
||||
///
|
||||
/// `clip-path` uses ShapeSource<BasicShape, GeometryBox>,
|
||||
/// `shape-outside` uses ShapeSource<BasicShape, ShapeBox>
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ShapeSource<B, T> {
|
||||
Url(SpecifiedUrl),
|
||||
Shape(B, Option<T>),
|
||||
Box(T),
|
||||
None,
|
||||
}
|
||||
|
||||
impl<B, T> HasViewportPercentage for ShapeSource<B, T> {
|
||||
impl Default for FillRule {
|
||||
#[inline]
|
||||
fn has_viewport_percentage(&self) -> bool { false }
|
||||
}
|
||||
|
||||
impl<B: ToCss, T: ToCss> ToCss for ShapeSource<B, T> {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
ShapeSource::Url(ref url) => url.to_css(dest),
|
||||
ShapeSource::Shape(ref shape, Some(ref ref_box)) => {
|
||||
shape.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
ref_box.to_css(dest)
|
||||
},
|
||||
ShapeSource::Shape(ref shape, None) => shape.to_css(dest),
|
||||
ShapeSource::Box(ref val) => val.to_css(dest),
|
||||
ShapeSource::None => dest.write_str("none"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Parse, T: Parse> Parse for ShapeSource<B, T> {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
if input.try(|i| i.expect_ident_matching("none")).is_ok() {
|
||||
return Ok(ShapeSource::None)
|
||||
}
|
||||
|
||||
if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
|
||||
return Ok(ShapeSource::Url(url))
|
||||
}
|
||||
|
||||
fn parse_component<U: Parse>(context: &ParserContext, input: &mut Parser,
|
||||
component: &mut Option<U>) -> bool {
|
||||
if component.is_some() {
|
||||
return false // already parsed this component
|
||||
}
|
||||
|
||||
*component = input.try(|i| U::parse(context, i)).ok();
|
||||
component.is_some()
|
||||
}
|
||||
|
||||
let mut shape = None;
|
||||
let mut ref_box = None;
|
||||
|
||||
while parse_component(context, input, &mut shape) ||
|
||||
parse_component(context, input, &mut ref_box) {
|
||||
//
|
||||
}
|
||||
|
||||
if let Some(shp) = shape {
|
||||
return Ok(ShapeSource::Shape(shp, ref_box))
|
||||
}
|
||||
|
||||
ref_box.map(|v| ShapeSource::Box(v)).ok_or(())
|
||||
}
|
||||
fn default() -> Self { FillRule::NonZero }
|
||||
}
|
||||
|
|
|
@ -10,120 +10,272 @@
|
|||
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::computed::{ComputedValueAsSpecified, Context, ToComputedValue};
|
||||
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, Position, PositionComponent};
|
||||
use values::specified::position::Side;
|
||||
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::{FillRule, BasicShape as GenericBasicShape};
|
||||
use values::generics::basic_shape::{FloatAreaShape as GenericFloatAreaShape, InsetRect as GenericInsetRect};
|
||||
use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
|
||||
use values::generics::basic_shape::{Polygon as GenericPolygon, ShapeRadius as GenericShapeRadius};
|
||||
use values::specified::{LengthOrPercentage, Percentage};
|
||||
use values::specified::position::{HorizontalPosition, Position, PositionComponent, Side, VerticalPosition};
|
||||
use values::specified::url::SpecifiedUrl;
|
||||
|
||||
/// The specified value used by `clip-path`
|
||||
pub type ShapeWithGeometryBox = ShapeSource<BasicShape, GeometryBox>;
|
||||
/// A specified clipping shape.
|
||||
pub type ClippingShape = GenericClippingShape<BasicShape>;
|
||||
|
||||
/// The specified value used by `shape-outside`
|
||||
pub type ShapeWithShapeBox = ShapeSource<BasicShape, ShapeBox>;
|
||||
/// A specified float area shape.
|
||||
pub type FloatAreaShape = GenericFloatAreaShape<BasicShape>;
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum BasicShape {
|
||||
Inset(InsetRect),
|
||||
Circle(Circle),
|
||||
Ellipse(Ellipse),
|
||||
Polygon(Polygon),
|
||||
/// A specified basic shape.
|
||||
pub type BasicShape = GenericBasicShape<HorizontalPosition, VerticalPosition, LengthOrPercentage>;
|
||||
|
||||
/// The specified value of `inset()`
|
||||
pub type InsetRect = GenericInsetRect<LengthOrPercentage>;
|
||||
|
||||
/// The specified value of `BorderRadius`
|
||||
pub type BorderRadius = GenericBorderRadius<LengthOrPercentage>;
|
||||
|
||||
/// A specified circle.
|
||||
pub type Circle = GenericCircle<HorizontalPosition, VerticalPosition, LengthOrPercentage>;
|
||||
|
||||
/// A specified ellipse.
|
||||
pub type Ellipse = GenericEllipse<HorizontalPosition, VerticalPosition, LengthOrPercentage>;
|
||||
|
||||
/// The specified value of `ShapeRadius`
|
||||
pub type ShapeRadius = GenericShapeRadius<LengthOrPercentage>;
|
||||
|
||||
/// The specified value of `Polygon`
|
||||
pub type Polygon = GenericPolygon<LengthOrPercentage>;
|
||||
|
||||
impl<ReferenceBox: Parse> Parse for ShapeSource<BasicShape, ReferenceBox> {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
if input.try(|i| i.expect_ident_matching("none")).is_ok() {
|
||||
return Ok(ShapeSource::None)
|
||||
}
|
||||
|
||||
if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
|
||||
return Ok(ShapeSource::Url(url))
|
||||
}
|
||||
|
||||
fn parse_component<U: Parse>(context: &ParserContext, input: &mut Parser,
|
||||
component: &mut Option<U>) -> bool {
|
||||
if component.is_some() {
|
||||
return false // already parsed this component
|
||||
}
|
||||
|
||||
*component = input.try(|i| U::parse(context, i)).ok();
|
||||
component.is_some()
|
||||
}
|
||||
|
||||
let mut shape = None;
|
||||
let mut ref_box = None;
|
||||
|
||||
while parse_component(context, input, &mut shape) ||
|
||||
parse_component(context, input, &mut ref_box) {
|
||||
//
|
||||
}
|
||||
|
||||
if let Some(shp) = shape {
|
||||
return Ok(ShapeSource::Shape(shp, ref_box))
|
||||
}
|
||||
|
||||
ref_box.map(|v| ShapeSource::Box(v)).ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for BasicShape {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<BasicShape, ()> {
|
||||
match_ignore_ascii_case! { &input.try(|i| i.expect_function())?,
|
||||
"inset" =>
|
||||
input.parse_nested_block(|i| InsetRect::parse_function_arguments(context, i))
|
||||
.map(BasicShape::Inset),
|
||||
"circle" =>
|
||||
input.parse_nested_block(|i| Circle::parse_function_arguments(context, i))
|
||||
.map(BasicShape::Circle),
|
||||
"ellipse" =>
|
||||
input.parse_nested_block(|i| Ellipse::parse_function_arguments(context, i))
|
||||
.map(BasicShape::Ellipse),
|
||||
"polygon" =>
|
||||
input.parse_nested_block(|i| Polygon::parse_function_arguments(context, i))
|
||||
.map(BasicShape::Polygon),
|
||||
impl Parse for GeometryBox {
|
||||
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
if let Ok(shape_box) = input.try(|i| ShapeBox::parse(i)) {
|
||||
return Ok(GeometryBox::ShapeBox(shape_box))
|
||||
}
|
||||
|
||||
match_ignore_ascii_case! { &input.expect_ident()?,
|
||||
"fill-box" => Ok(GeometryBox::FillBox),
|
||||
"stroke-box" => Ok(GeometryBox::StrokeBox),
|
||||
"view-box" => Ok(GeometryBox::ViewBox),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for BasicShape {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
BasicShape::Inset(ref rect) => rect.to_css(dest),
|
||||
BasicShape::Circle(ref circle) => circle.to_css(dest),
|
||||
BasicShape::Ellipse(ref e) => e.to_css(dest),
|
||||
BasicShape::Polygon(ref poly) => poly.to_css(dest),
|
||||
impl Parse for BasicShape {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
let function = input.expect_function()?;
|
||||
input.parse_nested_block(|i| {
|
||||
match_ignore_ascii_case! { &function,
|
||||
"inset" => InsetRect::parse_function_arguments(context, i).map(GenericBasicShape::Inset),
|
||||
"circle" => Circle::parse_function_arguments(context, i).map(GenericBasicShape::Circle),
|
||||
"ellipse" => Ellipse::parse_function_arguments(context, i).map(GenericBasicShape::Ellipse),
|
||||
"polygon" => Polygon::parse_function_arguments(context, i).map(GenericBasicShape::Polygon),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToComputedValue for BasicShape {
|
||||
type ComputedValue = computed_basic_shape::BasicShape;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
|
||||
match *self {
|
||||
BasicShape::Inset(ref rect) => computed_basic_shape::BasicShape::Inset(rect.to_computed_value(cx)),
|
||||
BasicShape::Circle(ref circle) => computed_basic_shape::BasicShape::Circle(circle.to_computed_value(cx)),
|
||||
BasicShape::Ellipse(ref e) => computed_basic_shape::BasicShape::Ellipse(e.to_computed_value(cx)),
|
||||
BasicShape::Polygon(ref poly) => computed_basic_shape::BasicShape::Polygon(poly.to_computed_value(cx)),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
match *computed {
|
||||
computed_basic_shape::BasicShape::Inset(ref rect) =>
|
||||
BasicShape::Inset(ToComputedValue::from_computed_value(rect)),
|
||||
computed_basic_shape::BasicShape::Circle(ref circle) =>
|
||||
BasicShape::Circle(ToComputedValue::from_computed_value(circle)),
|
||||
computed_basic_shape::BasicShape::Ellipse(ref e) =>
|
||||
BasicShape::Ellipse(ToComputedValue::from_computed_value(e)),
|
||||
computed_basic_shape::BasicShape::Polygon(ref poly) =>
|
||||
BasicShape::Polygon(ToComputedValue::from_computed_value(poly)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The specified value of `inset()`
|
||||
pub type InsetRect = GenericInsetRect<LengthOrPercentage>;
|
||||
|
||||
impl InsetRect {
|
||||
/// Parse the inner function arguments of `inset()`
|
||||
pub fn parse_function_arguments(context: &ParserContext, input: &mut Parser) -> Result<InsetRect, ()> {
|
||||
let (t, r, b, l) = parse_four_sides(input, |i| LengthOrPercentage::parse(context, i))?;
|
||||
let mut rect = GenericInsetRect {
|
||||
top: t,
|
||||
right: r,
|
||||
bottom: b,
|
||||
left: l,
|
||||
round: None,
|
||||
};
|
||||
|
||||
if input.try(|i| i.expect_ident_matching("round")).is_ok() {
|
||||
rect.round = Some(BorderRadius::parse(context, input)?);
|
||||
}
|
||||
|
||||
Ok(rect)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for InsetRect {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
match input.try(|i| i.expect_function()) {
|
||||
Ok(ref s) if s.eq_ignore_ascii_case("inset") =>
|
||||
input.parse_nested_block(|i| GenericInsetRect::parse_function_arguments(context, i)),
|
||||
input.expect_function_matching("inset")?;
|
||||
input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
|
||||
}
|
||||
}
|
||||
|
||||
impl InsetRect {
|
||||
/// Parse the inner function arguments of `inset()`
|
||||
pub fn parse_function_arguments(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
let (t, r, b, l) = parse_four_sides(input, |i| LengthOrPercentage::parse(context, i))?;
|
||||
let rect = if input.try(|i| i.expect_ident_matching("round")).is_ok() {
|
||||
Some(BorderRadius::parse(context, input)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(GenericInsetRect {
|
||||
top: t,
|
||||
right: r,
|
||||
bottom: b,
|
||||
left: l,
|
||||
round: rect,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
input.expect_function_matching("circle")?;
|
||||
input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
|
||||
}
|
||||
}
|
||||
|
||||
impl Circle {
|
||||
#[allow(missing_docs)]
|
||||
pub fn parse_function_arguments(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
let radius = input.try(|i| ShapeRadius::parse(context, i)).ok().unwrap_or_default();
|
||||
let position = if input.try(|i| i.expect_ident_matching("at")).is_ok() {
|
||||
Position::parse(context, input)?
|
||||
} else {
|
||||
Position::center()
|
||||
};
|
||||
|
||||
Ok(GenericCircle {
|
||||
radius: radius,
|
||||
position: position,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for Circle {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
dest.write_str("circle(")?;
|
||||
if GenericShapeRadius::ClosestSide != self.radius {
|
||||
self.radius.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
|
||||
dest.write_str("at ")?;
|
||||
serialize_basicshape_position(&self.position, dest)?;
|
||||
dest.write_str(")")
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Ellipse {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
input.expect_function_matching("ellipse")?;
|
||||
input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ellipse {
|
||||
#[allow(missing_docs)]
|
||||
pub fn parse_function_arguments(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
let (a, b) = input.try(|i| -> Result<_, ()> {
|
||||
Ok((ShapeRadius::parse(context, i)?, ShapeRadius::parse(context, i)?))
|
||||
}).ok().unwrap_or_default();
|
||||
let position = if input.try(|i| i.expect_ident_matching("at")).is_ok() {
|
||||
Position::parse(context, input)?
|
||||
} else {
|
||||
Position::center()
|
||||
};
|
||||
|
||||
Ok(GenericEllipse {
|
||||
semiaxis_x: a,
|
||||
semiaxis_y: b,
|
||||
position: position,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for Ellipse {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
dest.write_str("ellipse(")?;
|
||||
if self.semiaxis_x != ShapeRadius::default() || self.semiaxis_y != ShapeRadius::default() {
|
||||
self.semiaxis_x.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.semiaxis_y.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
|
||||
dest.write_str("at ")?;
|
||||
serialize_basicshape_position(&self.position, dest)?;
|
||||
dest.write_str(")")
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for ShapeRadius {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
|
||||
return Ok(GenericShapeRadius::Length(lop))
|
||||
}
|
||||
|
||||
match_ignore_ascii_case! { &input.expect_ident()?,
|
||||
"closest-side" => Ok(GenericShapeRadius::ClosestSide),
|
||||
"farthest-side" => Ok(GenericShapeRadius::FarthestSide),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
|
@ -196,266 +348,29 @@ fn serialize_basicshape_position<W>(position: &Position, dest: &mut W) -> fmt::R
|
|||
write_pair(&y_pos, &*y_lop, dest)
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
/// https://drafts.csswg.org/css-shapes/#funcdef-circle
|
||||
#[allow(missing_docs)]
|
||||
pub struct Circle {
|
||||
pub radius: ShapeRadius,
|
||||
pub position: Position,
|
||||
}
|
||||
|
||||
impl Circle {
|
||||
#[allow(missing_docs)]
|
||||
pub fn parse_function_arguments(context: &ParserContext, input: &mut Parser) -> Result<Circle, ()> {
|
||||
let radius = input.try(|i| ShapeRadius::parse(context, i)).ok().unwrap_or_default();
|
||||
let position = if input.try(|i| i.expect_ident_matching("at")).is_ok() {
|
||||
Position::parse(context, input)?
|
||||
} else {
|
||||
Position::center() // Defaults to origin
|
||||
};
|
||||
|
||||
Ok(Circle {
|
||||
radius: radius,
|
||||
position: position,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Circle {
|
||||
impl Parse for Polygon {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
match_ignore_ascii_case! { &try!(input.expect_function()),
|
||||
"circle" => {
|
||||
input.parse_nested_block(|i| Circle::parse_function_arguments(context, i))
|
||||
},
|
||||
_ => Err(())
|
||||
}
|
||||
input.expect_function_matching("polygon")?;
|
||||
input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for Circle {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
try!(dest.write_str("circle("));
|
||||
if GenericShapeRadius::ClosestSide != self.radius {
|
||||
try!(self.radius.to_css(dest));
|
||||
try!(dest.write_str(" "));
|
||||
}
|
||||
|
||||
try!(dest.write_str("at "));
|
||||
try!(serialize_basicshape_position(&self.position, dest));
|
||||
dest.write_str(")")
|
||||
}
|
||||
}
|
||||
|
||||
impl ToComputedValue for Circle {
|
||||
type ComputedValue = computed_basic_shape::Circle;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
|
||||
computed_basic_shape::Circle {
|
||||
radius: self.radius.to_computed_value(cx),
|
||||
position: self.position.to_computed_value(cx),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
Circle {
|
||||
radius: ToComputedValue::from_computed_value(&computed.radius),
|
||||
position: ToComputedValue::from_computed_value(&computed.position),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
/// https://drafts.csswg.org/css-shapes/#funcdef-ellipse
|
||||
#[allow(missing_docs)]
|
||||
pub struct Ellipse {
|
||||
pub semiaxis_x: ShapeRadius,
|
||||
pub semiaxis_y: ShapeRadius,
|
||||
pub position: Position,
|
||||
}
|
||||
|
||||
impl Ellipse {
|
||||
#[allow(missing_docs)]
|
||||
pub fn parse_function_arguments(context: &ParserContext, input: &mut Parser) -> Result<Ellipse, ()> {
|
||||
let (a, b) = input.try(|i| -> Result<_, ()> {
|
||||
Ok((ShapeRadius::parse(context, i)?, ShapeRadius::parse(context, i)?))
|
||||
impl Polygon {
|
||||
/// Parse the inner arguments of a `polygon` function.
|
||||
pub fn parse_function_arguments(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
let fill = input.try(|i| -> Result<_, ()> {
|
||||
let fill = FillRule::parse(i)?;
|
||||
i.expect_comma()?; // only eat the comma if there is something before it
|
||||
Ok(fill)
|
||||
}).ok().unwrap_or_default();
|
||||
let position = if input.try(|i| i.expect_ident_matching("at")).is_ok() {
|
||||
Position::parse(context, input)?
|
||||
} else {
|
||||
Position::center() // Defaults to origin
|
||||
};
|
||||
|
||||
Ok(Ellipse {
|
||||
semiaxis_x: a,
|
||||
semiaxis_y: b,
|
||||
position: position,
|
||||
let buf = input.parse_comma_separated(|i| {
|
||||
Ok((LengthOrPercentage::parse(context, i)?, LengthOrPercentage::parse(context, i)?))
|
||||
})?;
|
||||
|
||||
Ok(Polygon {
|
||||
fill: fill,
|
||||
coordinates: buf,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Ellipse {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
match input.try(|i| i.expect_function()) {
|
||||
Ok(ref s) if s.eq_ignore_ascii_case("ellipse") =>
|
||||
input.parse_nested_block(|i| Ellipse::parse_function_arguments(context, i)),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for Ellipse {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
try!(dest.write_str("ellipse("));
|
||||
if self.semiaxis_x != ShapeRadius::default() || self.semiaxis_y != ShapeRadius::default() {
|
||||
try!(self.semiaxis_x.to_css(dest));
|
||||
try!(dest.write_str(" "));
|
||||
try!(self.semiaxis_y.to_css(dest));
|
||||
try!(dest.write_str(" "));
|
||||
}
|
||||
|
||||
try!(dest.write_str("at "));
|
||||
try!(serialize_basicshape_position(&self.position, dest));
|
||||
dest.write_str(")")
|
||||
}
|
||||
}
|
||||
|
||||
impl ToComputedValue for Ellipse {
|
||||
type ComputedValue = computed_basic_shape::Ellipse;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
|
||||
computed_basic_shape::Ellipse {
|
||||
semiaxis_x: self.semiaxis_x.to_computed_value(cx),
|
||||
semiaxis_y: self.semiaxis_y.to_computed_value(cx),
|
||||
position: self.position.to_computed_value(cx),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
Ellipse {
|
||||
semiaxis_x: ToComputedValue::from_computed_value(&computed.semiaxis_x),
|
||||
semiaxis_y: ToComputedValue::from_computed_value(&computed.semiaxis_y),
|
||||
position: ToComputedValue::from_computed_value(&computed.position),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The specified value of `Polygon`
|
||||
pub type Polygon = GenericPolygon<LengthOrPercentage>;
|
||||
|
||||
/// The specified value of `ShapeRadius`
|
||||
pub type ShapeRadius = GenericShapeRadius<LengthOrPercentage>;
|
||||
|
||||
impl Parse for ShapeRadius {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
|
||||
return Ok(GenericShapeRadius::Length(lop))
|
||||
}
|
||||
|
||||
match_ignore_ascii_case! { &input.expect_ident()?,
|
||||
"closest-side" => Ok(GenericShapeRadius::ClosestSide),
|
||||
"farthest-side" => Ok(GenericShapeRadius::FarthestSide),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The specified value of `BorderRadius`
|
||||
pub type BorderRadius = GenericBorderRadius<LengthOrPercentage>;
|
||||
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box
|
||||
#[derive(Clone, PartialEq, Copy, Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum GeometryBox {
|
||||
FillBox,
|
||||
StrokeBox,
|
||||
ViewBox,
|
||||
ShapeBox(ShapeBox),
|
||||
}
|
||||
|
||||
impl Parse for GeometryBox {
|
||||
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
if let Ok(shape_box) = input.try(|i| ShapeBox::parse(i)) {
|
||||
return Ok(GeometryBox::ShapeBox(shape_box))
|
||||
}
|
||||
|
||||
match_ignore_ascii_case! { &input.expect_ident()?,
|
||||
"fill-box" => Ok(GeometryBox::FillBox),
|
||||
"stroke-box" => Ok(GeometryBox::StrokeBox),
|
||||
"view-box" => Ok(GeometryBox::ViewBox),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for GeometryBox {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
GeometryBox::FillBox => dest.write_str("fill-box"),
|
||||
GeometryBox::StrokeBox => dest.write_str("stroke-box"),
|
||||
GeometryBox::ViewBox => dest.write_str("view-box"),
|
||||
GeometryBox::ShapeBox(s) => s.to_css(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputedValueAsSpecified for GeometryBox {}
|
||||
|
||||
// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
|
||||
define_css_keyword_enum!(ShapeBox:
|
||||
"margin-box" => MarginBox,
|
||||
"border-box" => BorderBox,
|
||||
"padding-box" => PaddingBox,
|
||||
"content-box" => ContentBox
|
||||
);
|
||||
|
||||
add_impls_for_keyword_enum!(ShapeBox);
|
||||
|
|
Загрузка…
Ссылка в новой задаче