mirror of
https://github.com/servo/servo.git
synced 2025-06-25 09:34:32 +01:00
Refactor Gradient
In a similar way to Position, now specified and computed gradients share a common Gradient type defined in style::values::generics::image. This allows us to reuse most code for many style traits like ToCss, HasViewportPercentage and ToComputedValue. The test changes are the fallout of the disappearance of AngleOrCorner::None, which align our code to the spec for serialisation, where components that can be omitted should be omitted.
This commit is contained in:
parent
f2aaba685b
commit
abef5da9d8
10 changed files with 742 additions and 888 deletions
|
@ -58,11 +58,12 @@ use style::properties::longhands::border_image_repeat::computed_value::RepeatKey
|
||||||
use style::properties::style_structs;
|
use style::properties::style_structs;
|
||||||
use style::servo::restyle_damage::REPAINT;
|
use style::servo::restyle_damage::REPAINT;
|
||||||
use style::values::{Either, RGBA};
|
use style::values::{Either, RGBA};
|
||||||
use style::values::computed::{AngleOrCorner, Gradient, GradientItem, GradientKind, LengthOrPercentage};
|
use style::values::computed::{Gradient, GradientItem, LengthOrPercentage};
|
||||||
use style::values::computed::{LengthOrPercentageOrAuto, LengthOrKeyword, LengthOrPercentageOrKeyword};
|
use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position};
|
||||||
use style::values::computed::{NumberOrPercentage, Position};
|
use style::values::computed::image::{EndingShape, LineDirection};
|
||||||
use style::values::computed::image::{EndingShape, SizeKeyword};
|
use style::values::generics::image::{Circle, Ellipse, EndingShape as GenericEndingShape};
|
||||||
use style::values::generics::image::{GradientItem as GenericGradientItem, Image};
|
use style::values::generics::image::{GradientItem as GenericGradientItem, GradientKind};
|
||||||
|
use style::values::generics::image::{Image, ShapeExtent};
|
||||||
use style::values::specified::position::{X, Y};
|
use style::values::specified::position::{X, Y};
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
use style_traits::cursor::Cursor;
|
use style_traits::cursor::Cursor;
|
||||||
|
@ -391,7 +392,7 @@ pub trait FragmentDisplayListBuilding {
|
||||||
fn convert_linear_gradient(&self,
|
fn convert_linear_gradient(&self,
|
||||||
bounds: &Rect<Au>,
|
bounds: &Rect<Au>,
|
||||||
stops: &[GradientItem],
|
stops: &[GradientItem],
|
||||||
angle_or_corner: &AngleOrCorner,
|
direction: &LineDirection,
|
||||||
repeating: bool,
|
repeating: bool,
|
||||||
style: &ServoComputedValues)
|
style: &ServoComputedValues)
|
||||||
-> display_list::Gradient;
|
-> display_list::Gradient;
|
||||||
|
@ -759,36 +760,46 @@ fn get_ellipse_radius<F>(size: &Size2D<Au>, center: &Point2D<Au>, cmp: F) -> Siz
|
||||||
|
|
||||||
/// Determines the radius of a circle if it was not explictly provided.
|
/// Determines the radius of a circle if it was not explictly provided.
|
||||||
/// https://drafts.csswg.org/css-images-3/#typedef-size
|
/// https://drafts.csswg.org/css-images-3/#typedef-size
|
||||||
fn convert_circle_size_keyword(keyword: SizeKeyword,
|
fn convert_circle_size_keyword(keyword: ShapeExtent,
|
||||||
size: &Size2D<Au>,
|
size: &Size2D<Au>,
|
||||||
center: &Point2D<Au>) -> Size2D<Au> {
|
center: &Point2D<Au>) -> Size2D<Au> {
|
||||||
use style::values::computed::image::SizeKeyword::*;
|
|
||||||
let radius = match keyword {
|
let radius = match keyword {
|
||||||
ClosestSide | Contain => {
|
ShapeExtent::ClosestSide | ShapeExtent::Contain => {
|
||||||
let dist = get_distance_to_sides(size, center, ::std::cmp::min);
|
let dist = get_distance_to_sides(size, center, ::std::cmp::min);
|
||||||
::std::cmp::min(dist.width, dist.height)
|
::std::cmp::min(dist.width, dist.height)
|
||||||
}
|
}
|
||||||
FarthestSide => {
|
ShapeExtent::FarthestSide => {
|
||||||
let dist = get_distance_to_sides(size, center, ::std::cmp::max);
|
let dist = get_distance_to_sides(size, center, ::std::cmp::max);
|
||||||
::std::cmp::max(dist.width, dist.height)
|
::std::cmp::max(dist.width, dist.height)
|
||||||
}
|
}
|
||||||
ClosestCorner => get_distance_to_corner(size, center, ::std::cmp::min),
|
ShapeExtent::ClosestCorner => {
|
||||||
FarthestCorner | Cover => get_distance_to_corner(size, center, ::std::cmp::max),
|
get_distance_to_corner(size, center, ::std::cmp::min)
|
||||||
|
},
|
||||||
|
ShapeExtent::FarthestCorner | ShapeExtent::Cover => {
|
||||||
|
get_distance_to_corner(size, center, ::std::cmp::max)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
Size2D::new(radius, radius)
|
Size2D::new(radius, radius)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines the radius of an ellipse if it was not explictly provided.
|
/// Determines the radius of an ellipse if it was not explictly provided.
|
||||||
/// https://drafts.csswg.org/css-images-3/#typedef-size
|
/// https://drafts.csswg.org/css-images-3/#typedef-size
|
||||||
fn convert_ellipse_size_keyword(keyword: SizeKeyword,
|
fn convert_ellipse_size_keyword(keyword: ShapeExtent,
|
||||||
size: &Size2D<Au>,
|
size: &Size2D<Au>,
|
||||||
center: &Point2D<Au>) -> Size2D<Au> {
|
center: &Point2D<Au>) -> Size2D<Au> {
|
||||||
use style::values::computed::image::SizeKeyword::*;
|
|
||||||
match keyword {
|
match keyword {
|
||||||
ClosestSide | Contain => get_distance_to_sides(size, center, ::std::cmp::min),
|
ShapeExtent::ClosestSide | ShapeExtent::Contain => {
|
||||||
FarthestSide => get_distance_to_sides(size, center, ::std::cmp::max),
|
get_distance_to_sides(size, center, ::std::cmp::min)
|
||||||
ClosestCorner => get_ellipse_radius(size, center, ::std::cmp::min),
|
},
|
||||||
FarthestCorner | Cover => get_ellipse_radius(size, center, ::std::cmp::max),
|
ShapeExtent::FarthestSide => {
|
||||||
|
get_distance_to_sides(size, center, ::std::cmp::max)
|
||||||
|
},
|
||||||
|
ShapeExtent::ClosestCorner => {
|
||||||
|
get_ellipse_radius(size, center, ::std::cmp::min)
|
||||||
|
},
|
||||||
|
ShapeExtent::FarthestCorner | ShapeExtent::Cover => {
|
||||||
|
get_ellipse_radius(size, center, ::std::cmp::max)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1098,13 +1109,13 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
fn convert_linear_gradient(&self,
|
fn convert_linear_gradient(&self,
|
||||||
bounds: &Rect<Au>,
|
bounds: &Rect<Au>,
|
||||||
stops: &[GradientItem],
|
stops: &[GradientItem],
|
||||||
angle_or_corner: &AngleOrCorner,
|
direction: &LineDirection,
|
||||||
repeating: bool,
|
repeating: bool,
|
||||||
style: &ServoComputedValues)
|
style: &ServoComputedValues)
|
||||||
-> display_list::Gradient {
|
-> display_list::Gradient {
|
||||||
let angle = match *angle_or_corner {
|
let angle = match *direction {
|
||||||
AngleOrCorner::Angle(angle) => angle.radians(),
|
LineDirection::Angle(angle) => angle.radians(),
|
||||||
AngleOrCorner::Corner(horizontal, vertical) => {
|
LineDirection::Corner(horizontal, vertical) => {
|
||||||
// This the angle for one of the diagonals of the box. Our angle
|
// This the angle for one of the diagonals of the box. Our angle
|
||||||
// will either be this one, this one + PI, or one of the other
|
// will either be this one, this one + PI, or one of the other
|
||||||
// two perpendicular angles.
|
// two perpendicular angles.
|
||||||
|
@ -1171,16 +1182,18 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
let center = Point2D::new(specified(center.horizontal, bounds.size.width),
|
let center = Point2D::new(specified(center.horizontal, bounds.size.width),
|
||||||
specified(center.vertical, bounds.size.height));
|
specified(center.vertical, bounds.size.height));
|
||||||
let radius = match *shape {
|
let radius = match *shape {
|
||||||
EndingShape::Circle(LengthOrKeyword::Length(length))
|
GenericEndingShape::Circle(Circle::Radius(length)) => {
|
||||||
=> Size2D::new(length, length),
|
Size2D::new(length, length)
|
||||||
EndingShape::Circle(LengthOrKeyword::Keyword(word))
|
},
|
||||||
=> convert_circle_size_keyword(word, &bounds.size, ¢er),
|
GenericEndingShape::Circle(Circle::Extent(extent)) => {
|
||||||
EndingShape::Ellipse(LengthOrPercentageOrKeyword::LengthOrPercentage(horizontal,
|
convert_circle_size_keyword(extent, &bounds.size, ¢er)
|
||||||
vertical))
|
},
|
||||||
=> Size2D::new(specified(horizontal, bounds.size.width),
|
GenericEndingShape::Ellipse(Ellipse::Radii(x, y)) => {
|
||||||
specified(vertical, bounds.size.height)),
|
Size2D::new(specified(x, bounds.size.width), specified(y, bounds.size.height))
|
||||||
EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(word))
|
},
|
||||||
=> convert_ellipse_size_keyword(word, &bounds.size, ¢er),
|
GenericEndingShape::Ellipse(Ellipse::Extent(extent)) => {
|
||||||
|
convert_ellipse_size_keyword(extent, &bounds.size, ¢er)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut stops = convert_gradient_stops(stops, radius.width, style);
|
let mut stops = convert_gradient_stops(stops, radius.width, style);
|
||||||
|
|
|
@ -192,9 +192,8 @@ impl nsStyleImage {
|
||||||
use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE};
|
use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE};
|
||||||
use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE};
|
use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE};
|
||||||
use gecko_bindings::structs::nsStyleCoord;
|
use gecko_bindings::structs::nsStyleCoord;
|
||||||
use values::computed::{AngleOrCorner, GradientKind, GradientShape, LengthOrKeyword};
|
use values::computed::image::LineDirection;
|
||||||
use values::computed::LengthOrPercentageOrKeyword;
|
use values::generics::image::{Circle, Ellipse, EndingShape, GradientKind, ShapeExtent};
|
||||||
use values::specified::SizeKeyword;
|
|
||||||
use values::specified::position::{X, Y};
|
use values::specified::position::{X, Y};
|
||||||
|
|
||||||
let stop_count = gradient.items.len();
|
let stop_count = gradient.items.len();
|
||||||
|
@ -204,7 +203,7 @@ impl nsStyleImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
let gecko_gradient = match gradient.kind {
|
let gecko_gradient = match gradient.kind {
|
||||||
GradientKind::Linear(angle_or_corner) => {
|
GradientKind::Linear(direction) => {
|
||||||
let gecko_gradient = unsafe {
|
let gecko_gradient = unsafe {
|
||||||
Gecko_CreateGradient(NS_STYLE_GRADIENT_SHAPE_LINEAR as u8,
|
Gecko_CreateGradient(NS_STYLE_GRADIENT_SHAPE_LINEAR as u8,
|
||||||
NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as u8,
|
NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as u8,
|
||||||
|
@ -213,15 +212,15 @@ impl nsStyleImage {
|
||||||
stop_count as u32)
|
stop_count as u32)
|
||||||
};
|
};
|
||||||
|
|
||||||
match angle_or_corner {
|
match direction {
|
||||||
AngleOrCorner::Angle(angle) => {
|
LineDirection::Angle(angle) => {
|
||||||
unsafe {
|
unsafe {
|
||||||
(*gecko_gradient).mAngle.set(angle);
|
(*gecko_gradient).mAngle.set(angle);
|
||||||
(*gecko_gradient).mBgPosX.set_value(CoordDataValue::None);
|
(*gecko_gradient).mBgPosX.set_value(CoordDataValue::None);
|
||||||
(*gecko_gradient).mBgPosY.set_value(CoordDataValue::None);
|
(*gecko_gradient).mBgPosY.set_value(CoordDataValue::None);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
AngleOrCorner::Corner(horiz, vert) => {
|
LineDirection::Corner(horiz, vert) => {
|
||||||
let percent_x = match horiz {
|
let percent_x = match horiz {
|
||||||
X::Left => 0.0,
|
X::Left => 0.0,
|
||||||
X::Right => 1.0,
|
X::Right => 1.0,
|
||||||
|
@ -245,28 +244,28 @@ impl nsStyleImage {
|
||||||
GradientKind::Radial(shape, position) => {
|
GradientKind::Radial(shape, position) => {
|
||||||
let keyword_to_gecko_size = |keyword| {
|
let keyword_to_gecko_size = |keyword| {
|
||||||
match keyword {
|
match keyword {
|
||||||
SizeKeyword::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE,
|
ShapeExtent::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE,
|
||||||
SizeKeyword::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE,
|
ShapeExtent::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE,
|
||||||
SizeKeyword::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER,
|
ShapeExtent::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER,
|
||||||
SizeKeyword::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER,
|
ShapeExtent::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER,
|
||||||
SizeKeyword::Contain => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE,
|
ShapeExtent::Contain => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE,
|
||||||
SizeKeyword::Cover => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER,
|
ShapeExtent::Cover => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (gecko_shape, gecko_size) = match shape {
|
let (gecko_shape, gecko_size) = match shape {
|
||||||
GradientShape::Circle(ref length) => {
|
EndingShape::Circle(ref circle) => {
|
||||||
let size = match *length {
|
let size = match *circle {
|
||||||
LengthOrKeyword::Keyword(keyword) => {
|
Circle::Extent(extent) => {
|
||||||
keyword_to_gecko_size(keyword)
|
keyword_to_gecko_size(extent)
|
||||||
},
|
},
|
||||||
_ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE,
|
_ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE,
|
||||||
};
|
};
|
||||||
(NS_STYLE_GRADIENT_SHAPE_CIRCULAR as u8, size as u8)
|
(NS_STYLE_GRADIENT_SHAPE_CIRCULAR as u8, size as u8)
|
||||||
},
|
},
|
||||||
GradientShape::Ellipse(ref length) => {
|
EndingShape::Ellipse(ref ellipse) => {
|
||||||
let size = match *length {
|
let size = match *ellipse {
|
||||||
LengthOrPercentageOrKeyword::Keyword(keyword) => {
|
Ellipse::Extent(extent) => {
|
||||||
keyword_to_gecko_size(keyword)
|
keyword_to_gecko_size(extent)
|
||||||
},
|
},
|
||||||
_ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE,
|
_ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE,
|
||||||
};
|
};
|
||||||
|
@ -291,22 +290,19 @@ impl nsStyleImage {
|
||||||
|
|
||||||
// Setting radius values depending shape
|
// Setting radius values depending shape
|
||||||
match shape {
|
match shape {
|
||||||
GradientShape::Circle(length) => {
|
EndingShape::Circle(Circle::Radius(length)) => {
|
||||||
if let LengthOrKeyword::Length(len) = length {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
(*gecko_gradient).mRadiusX.set_value(CoordDataValue::Coord(len.0));
|
(*gecko_gradient).mRadiusX.set_value(CoordDataValue::Coord(length.0));
|
||||||
(*gecko_gradient).mRadiusY.set_value(CoordDataValue::Coord(len.0));
|
(*gecko_gradient).mRadiusY.set_value(CoordDataValue::Coord(length.0));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
GradientShape::Ellipse(length) => {
|
EndingShape::Ellipse(Ellipse::Radii(x, y)) => {
|
||||||
if let LengthOrPercentageOrKeyword::LengthOrPercentage(first_len, second_len) = length {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
(*gecko_gradient).mRadiusX.set(first_len);
|
(*gecko_gradient).mRadiusX.set(x);
|
||||||
(*gecko_gradient).mRadiusY.set(second_len);
|
(*gecko_gradient).mRadiusY.set(y);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
(*gecko_gradient).mBgPosX.set(position.horizontal);
|
(*gecko_gradient).mBgPosX.set(position.horizontal);
|
||||||
|
|
|
@ -11,23 +11,49 @@ use cssparser::Color as CSSColor;
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use values::generics::image::{CompatMode, ColorStop as GenericColorStop};
|
|
||||||
use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem};
|
|
||||||
use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect};
|
|
||||||
use values::computed::{Angle, Context, Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue};
|
use values::computed::{Angle, Context, Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue};
|
||||||
use values::computed::position::Position;
|
use values::computed::position::Position;
|
||||||
use values::specified;
|
use values::generics::image::{CompatMode, ColorStop as GenericColorStop, EndingShape as GenericEndingShape};
|
||||||
|
use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem};
|
||||||
|
use values::generics::image::{Image as GenericImage, GradientKind as GenericGradientKind};
|
||||||
|
use values::generics::image::{ImageRect as GenericImageRect, LineDirection as GenericLineDirection};
|
||||||
|
use values::specified::image::LineDirection as SpecifiedLineDirection;
|
||||||
use values::specified::position::{X, Y};
|
use values::specified::position::{X, Y};
|
||||||
|
|
||||||
pub use values::specified::SizeKeyword;
|
|
||||||
|
|
||||||
/// Computed values for an image according to CSS-IMAGES.
|
/// Computed values for an image according to CSS-IMAGES.
|
||||||
/// https://drafts.csswg.org/css-images/#image-values
|
/// https://drafts.csswg.org/css-images/#image-values
|
||||||
pub type Image = GenericImage<Gradient, NumberOrPercentage>;
|
pub type Image = GenericImage<Gradient, ImageRect>;
|
||||||
|
|
||||||
/// Computed values for a CSS gradient.
|
/// Computed values for a CSS gradient.
|
||||||
/// https://drafts.csswg.org/css-images/#gradients
|
/// https://drafts.csswg.org/css-images/#gradients
|
||||||
pub type Gradient = GenericGradient<GradientKind, CSSColor, LengthOrPercentage>;
|
pub type Gradient = GenericGradient<
|
||||||
|
LineDirection,
|
||||||
|
Length,
|
||||||
|
LengthOrPercentage,
|
||||||
|
Position,
|
||||||
|
CSSColor,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// A computed gradient kind.
|
||||||
|
pub type GradientKind = GenericGradientKind<
|
||||||
|
LineDirection,
|
||||||
|
Length,
|
||||||
|
LengthOrPercentage,
|
||||||
|
Position,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// A computed gradient line direction.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub enum LineDirection {
|
||||||
|
/// An angle.
|
||||||
|
Angle(Angle),
|
||||||
|
/// A corner.
|
||||||
|
Corner(X, Y),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A computed radial gradient ending shape.
|
||||||
|
pub type EndingShape = GenericEndingShape<Length, LengthOrPercentage>;
|
||||||
|
|
||||||
/// A computed gradient item.
|
/// A computed gradient item.
|
||||||
pub type GradientItem = GenericGradientItem<CSSColor, LengthOrPercentage>;
|
pub type GradientItem = GenericGradientItem<CSSColor, LengthOrPercentage>;
|
||||||
|
@ -38,359 +64,69 @@ pub type ColorStop = GenericColorStop<CSSColor, LengthOrPercentage>;
|
||||||
/// Computed values for ImageRect.
|
/// Computed values for ImageRect.
|
||||||
pub type ImageRect = GenericImageRect<NumberOrPercentage>;
|
pub type ImageRect = GenericImageRect<NumberOrPercentage>;
|
||||||
|
|
||||||
impl ToCss for Gradient {
|
impl GenericLineDirection for LineDirection {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
fn points_downwards(&self) -> bool {
|
||||||
if self.compat_mode == CompatMode::WebKit {
|
|
||||||
try!(dest.write_str("-webkit-"));
|
|
||||||
}
|
|
||||||
if self.repeating {
|
|
||||||
try!(dest.write_str("repeating-"));
|
|
||||||
}
|
|
||||||
match self.kind {
|
|
||||||
GradientKind::Linear(angle_or_corner) => {
|
|
||||||
try!(dest.write_str("linear-gradient("));
|
|
||||||
try!(angle_or_corner.to_css(dest, self.compat_mode));
|
|
||||||
},
|
|
||||||
GradientKind::Radial(ref shape, position) => {
|
|
||||||
try!(dest.write_str("radial-gradient("));
|
|
||||||
try!(shape.to_css(dest));
|
|
||||||
try!(dest.write_str(" at "));
|
|
||||||
try!(position.to_css(dest));
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for item in &self.items {
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
try!(item.to_css(dest));
|
|
||||||
}
|
|
||||||
try!(dest.write_str(")"));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Gradient {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self.kind {
|
|
||||||
GradientKind::Linear(angle_or_corner) => {
|
|
||||||
let _ = write!(f, "{:?}", angle_or_corner);
|
|
||||||
},
|
|
||||||
GradientKind::Radial(ref shape, position) => {
|
|
||||||
let _ = write!(f, "{:?} at {:?}", shape, position);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for item in &self.items {
|
|
||||||
let _ = write!(f, ", {:?}", item);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computed values for CSS linear or radial gradients.
|
|
||||||
/// https://drafts.csswg.org/css-images/#gradients
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum GradientKind {
|
|
||||||
Linear(AngleOrCorner),
|
|
||||||
Radial(EndingShape, Position),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for specified::GradientKind {
|
|
||||||
type ComputedValue = GradientKind;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_computed_value(&self, context: &Context) -> GradientKind {
|
|
||||||
match *self {
|
match *self {
|
||||||
specified::GradientKind::Linear(angle_or_corner) => {
|
LineDirection::Angle(angle) => angle.radians() == PI,
|
||||||
GradientKind::Linear(angle_or_corner.to_computed_value(context))
|
LineDirection::Corner(..) => false,
|
||||||
},
|
}
|
||||||
specified::GradientKind::Radial(ref shape, ref position) => {
|
}
|
||||||
GradientKind::Radial(shape.to_computed_value(context),
|
|
||||||
position.to_computed_value(context))
|
fn to_css<W>(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result
|
||||||
|
where W: fmt::Write
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
LineDirection::Angle(ref angle) => angle.to_css(dest),
|
||||||
|
LineDirection::Corner(x, y) => {
|
||||||
|
if compat_mode == CompatMode::Modern {
|
||||||
|
dest.write_str("to ")?;
|
||||||
|
}
|
||||||
|
x.to_css(dest)?;
|
||||||
|
dest.write_str(" ")?;
|
||||||
|
y.to_css(dest)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline]
|
}
|
||||||
fn from_computed_value(computed: &GradientKind) -> Self {
|
|
||||||
|
impl ToComputedValue for SpecifiedLineDirection {
|
||||||
|
type ComputedValue = LineDirection;
|
||||||
|
|
||||||
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
|
match *self {
|
||||||
|
SpecifiedLineDirection::Angle(ref angle) => {
|
||||||
|
LineDirection::Angle(angle.to_computed_value(context))
|
||||||
|
},
|
||||||
|
SpecifiedLineDirection::Horizontal(X::Left) => {
|
||||||
|
LineDirection::Angle(Angle::Degree(270.))
|
||||||
|
},
|
||||||
|
SpecifiedLineDirection::Horizontal(X::Right) => {
|
||||||
|
LineDirection::Angle(Angle::Degree(90.))
|
||||||
|
},
|
||||||
|
SpecifiedLineDirection::Vertical(Y::Top) => {
|
||||||
|
LineDirection::Angle(Angle::Degree(0.))
|
||||||
|
},
|
||||||
|
SpecifiedLineDirection::Vertical(Y::Bottom) => {
|
||||||
|
LineDirection::Angle(Angle::Degree(180.))
|
||||||
|
},
|
||||||
|
SpecifiedLineDirection::Corner(x, y) => {
|
||||||
|
LineDirection::Corner(x, y)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||||
match *computed {
|
match *computed {
|
||||||
GradientKind::Linear(angle_or_corner) => {
|
LineDirection::Angle(ref angle) => {
|
||||||
specified::GradientKind::Linear(ToComputedValue::from_computed_value(&angle_or_corner))
|
SpecifiedLineDirection::Angle(ToComputedValue::from_computed_value(angle))
|
||||||
},
|
},
|
||||||
GradientKind::Radial(ref shape, position) => {
|
LineDirection::Corner(x, y) => {
|
||||||
specified::GradientKind::Radial(ToComputedValue::from_computed_value(shape),
|
SpecifiedLineDirection::Corner(x, y)
|
||||||
ToComputedValue::from_computed_value(&position))
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computed values for EndingShape
|
|
||||||
/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-ending-shape
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum EndingShape {
|
|
||||||
Circle(LengthOrKeyword),
|
|
||||||
Ellipse(LengthOrPercentageOrKeyword),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for EndingShape {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
match *self {
|
|
||||||
EndingShape::Circle(ref length) => {
|
|
||||||
try!(dest.write_str("circle "));
|
|
||||||
try!(length.to_css(dest));
|
|
||||||
},
|
|
||||||
EndingShape::Ellipse(ref length) => {
|
|
||||||
try!(dest.write_str("ellipse "));
|
|
||||||
try!(length.to_css(dest));
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for EndingShape {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
EndingShape::Circle(ref length) => {
|
|
||||||
let _ = write!(f, "circle {:?}", length);
|
|
||||||
},
|
|
||||||
EndingShape::Ellipse(ref length) => {
|
|
||||||
let _ = write!(f, "ellipse {:?}", length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for specified::GradientEndingShape {
|
|
||||||
type ComputedValue = EndingShape;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_computed_value(&self, context: &Context) -> EndingShape {
|
|
||||||
match *self {
|
|
||||||
specified::GradientEndingShape::Circle(ref length) => {
|
|
||||||
EndingShape::Circle(length.to_computed_value(context))
|
|
||||||
},
|
|
||||||
specified::GradientEndingShape::Ellipse(ref length) => {
|
|
||||||
EndingShape::Ellipse(length.to_computed_value(context))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn from_computed_value(computed: &EndingShape) -> Self {
|
|
||||||
match *computed {
|
|
||||||
EndingShape::Circle(ref length) => {
|
|
||||||
specified::GradientEndingShape::Circle(ToComputedValue::from_computed_value(length))
|
|
||||||
},
|
|
||||||
EndingShape::Ellipse(ref length) => {
|
|
||||||
specified::GradientEndingShape::Ellipse(ToComputedValue::from_computed_value(length))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum LengthOrKeyword {
|
|
||||||
Length(Length),
|
|
||||||
Keyword(SizeKeyword),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for LengthOrKeyword {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
match *self {
|
|
||||||
LengthOrKeyword::Length(ref length) => length.to_css(dest),
|
|
||||||
LengthOrKeyword::Keyword(keyword) => keyword.to_css(dest),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for LengthOrKeyword {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
LengthOrKeyword::Length(ref length) => {
|
|
||||||
let _ = write!(f, "{:?}", length);
|
|
||||||
},
|
|
||||||
LengthOrKeyword::Keyword(keyword) => {
|
|
||||||
let _ = write!(f, "{:?}", keyword);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for specified::LengthOrKeyword {
|
|
||||||
type ComputedValue = LengthOrKeyword;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_computed_value(&self, context: &Context) -> LengthOrKeyword {
|
|
||||||
match *self {
|
|
||||||
specified::LengthOrKeyword::Length(ref length) => {
|
|
||||||
LengthOrKeyword::Length(length.to_computed_value(context))
|
|
||||||
},
|
|
||||||
specified::LengthOrKeyword::Keyword(keyword) => {
|
|
||||||
LengthOrKeyword::Keyword(keyword)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn from_computed_value(computed: &LengthOrKeyword) -> Self {
|
|
||||||
match *computed {
|
|
||||||
LengthOrKeyword::Length(length) => {
|
|
||||||
specified::LengthOrKeyword::Length(ToComputedValue::from_computed_value(&length))
|
|
||||||
},
|
|
||||||
LengthOrKeyword::Keyword(keyword) => {
|
|
||||||
specified::LengthOrKeyword::Keyword(keyword)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum LengthOrPercentageOrKeyword {
|
|
||||||
LengthOrPercentage(LengthOrPercentage, LengthOrPercentage),
|
|
||||||
Keyword(SizeKeyword),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for LengthOrPercentageOrKeyword {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
match *self {
|
|
||||||
LengthOrPercentageOrKeyword::LengthOrPercentage(ref first_len, second_len) => {
|
|
||||||
try!(first_len.to_css(dest));
|
|
||||||
try!(dest.write_str(" "));
|
|
||||||
second_len.to_css(dest)
|
|
||||||
},
|
|
||||||
LengthOrPercentageOrKeyword::Keyword(keyword) => keyword.to_css(dest),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for LengthOrPercentageOrKeyword {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
LengthOrPercentageOrKeyword::LengthOrPercentage(ref first_len, second_len) => {
|
|
||||||
let _ = write!(f, "{:?} {:?}", first_len, second_len);
|
|
||||||
},
|
|
||||||
LengthOrPercentageOrKeyword::Keyword(keyword) => {
|
|
||||||
let _ = write!(f, "{:?}", keyword);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for specified::LengthOrPercentageOrKeyword {
|
|
||||||
type ComputedValue = LengthOrPercentageOrKeyword;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrKeyword {
|
|
||||||
match *self {
|
|
||||||
specified::LengthOrPercentageOrKeyword::LengthOrPercentage(ref first_len, ref second_len) => {
|
|
||||||
LengthOrPercentageOrKeyword::LengthOrPercentage(first_len.to_computed_value(context),
|
|
||||||
second_len.to_computed_value(context))
|
|
||||||
},
|
|
||||||
specified::LengthOrPercentageOrKeyword::Keyword(keyword) => {
|
|
||||||
LengthOrPercentageOrKeyword::Keyword(keyword)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn from_computed_value(computed: &LengthOrPercentageOrKeyword) -> Self {
|
|
||||||
match *computed {
|
|
||||||
LengthOrPercentageOrKeyword::LengthOrPercentage(first_len, second_len) => {
|
|
||||||
specified::LengthOrPercentageOrKeyword::LengthOrPercentage(
|
|
||||||
ToComputedValue::from_computed_value(&first_len),
|
|
||||||
ToComputedValue::from_computed_value(&second_len))
|
|
||||||
},
|
|
||||||
LengthOrPercentageOrKeyword::Keyword(keyword) => {
|
|
||||||
specified::LengthOrPercentageOrKeyword::Keyword(keyword)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum AngleOrCorner {
|
|
||||||
Angle(Angle),
|
|
||||||
Corner(X, Y)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for specified::AngleOrCorner {
|
|
||||||
type ComputedValue = AngleOrCorner;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_computed_value(&self, context: &Context) -> AngleOrCorner {
|
|
||||||
match *self {
|
|
||||||
specified::AngleOrCorner::None => {
|
|
||||||
AngleOrCorner::Angle(Angle::from_radians(PI))
|
|
||||||
},
|
|
||||||
specified::AngleOrCorner::Angle(angle) => {
|
|
||||||
AngleOrCorner::Angle(angle.to_computed_value(context))
|
|
||||||
},
|
|
||||||
specified::AngleOrCorner::Corner(horizontal, vertical) => {
|
|
||||||
match (horizontal, vertical) {
|
|
||||||
(None, Some(Y::Top)) => {
|
|
||||||
AngleOrCorner::Angle(Angle::from_radians(0.0))
|
|
||||||
},
|
|
||||||
(Some(X::Right), None) => {
|
|
||||||
AngleOrCorner::Angle(Angle::from_radians(PI * 0.5))
|
|
||||||
},
|
|
||||||
(None, Some(Y::Bottom)) => {
|
|
||||||
AngleOrCorner::Angle(Angle::from_radians(PI))
|
|
||||||
},
|
|
||||||
(Some(X::Left), None) => {
|
|
||||||
AngleOrCorner::Angle(Angle::from_radians(PI * 1.5))
|
|
||||||
},
|
|
||||||
(Some(horizontal), Some(vertical)) => {
|
|
||||||
AngleOrCorner::Corner(horizontal, vertical)
|
|
||||||
},
|
|
||||||
(None, None) => {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_computed_value(computed: &AngleOrCorner) -> Self {
|
|
||||||
match *computed {
|
|
||||||
AngleOrCorner::Angle(ref angle) => {
|
|
||||||
specified::AngleOrCorner::Angle(specified::Angle::from_computed_value(angle))
|
|
||||||
},
|
|
||||||
AngleOrCorner::Corner(horizontal, vertical) => {
|
|
||||||
specified::AngleOrCorner::Corner(Some(horizontal), Some(vertical))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AngleOrCorner {
|
|
||||||
fn to_css<W>(&self, dest: &mut W, mode: CompatMode) -> fmt::Result where W: fmt::Write {
|
|
||||||
match *self {
|
|
||||||
AngleOrCorner::Angle(angle) => angle.to_css(dest),
|
|
||||||
AngleOrCorner::Corner(horizontal, vertical) => {
|
|
||||||
if mode == CompatMode::Modern {
|
|
||||||
try!(dest.write_str("to "));
|
|
||||||
}
|
|
||||||
try!(horizontal.to_css(dest));
|
|
||||||
try!(dest.write_str(" "));
|
|
||||||
try!(vertical.to_css(dest));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computed values for none | <image> | <mask-source>.
|
/// Computed values for none | <image> | <mask-source>.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
|
|
@ -12,8 +12,7 @@ use super::{Number, ToComputedValue, Context};
|
||||||
use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified};
|
use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified};
|
||||||
use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength, ViewportPercentageLength};
|
use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength, ViewportPercentageLength};
|
||||||
|
|
||||||
pub use super::image::{EndingShape as GradientShape, Gradient, GradientKind, Image};
|
pub use super::image::Image;
|
||||||
pub use super::image::{LengthOrKeyword, LengthOrPercentageOrKeyword};
|
|
||||||
pub use values::specified::{Angle, BorderStyle, Time, UrlOrNone};
|
pub use values::specified::{Angle, BorderStyle, Time, UrlOrNone};
|
||||||
|
|
||||||
impl ToComputedValue for specified::NoCalcLength {
|
impl ToComputedValue for specified::NoCalcLength {
|
||||||
|
|
|
@ -22,8 +22,7 @@ use super::specified::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as G
|
||||||
|
|
||||||
pub use app_units::Au;
|
pub use app_units::Au;
|
||||||
pub use cssparser::Color as CSSColor;
|
pub use cssparser::Color as CSSColor;
|
||||||
pub use self::image::{AngleOrCorner, EndingShape as GradientShape, Gradient, GradientItem, LayerImage};
|
pub use self::image::{Gradient, GradientItem, LayerImage, LineDirection, Image, ImageRect};
|
||||||
pub use self::image::{GradientKind, Image, ImageRect, LengthOrKeyword, LengthOrPercentageOrKeyword};
|
|
||||||
pub use super::{Auto, Either, None_};
|
pub use super::{Auto, Either, None_};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
|
pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
|
||||||
|
|
|
@ -19,13 +19,13 @@ use values::specified::url::SpecifiedUrl;
|
||||||
/// [image]: https://drafts.csswg.org/css-images/#image-values
|
/// [image]: https://drafts.csswg.org/css-images/#image-values
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub enum Image<G, N> {
|
pub enum Image<Gradient, ImageRect> {
|
||||||
/// A `<url()>` image.
|
/// A `<url()>` image.
|
||||||
Url(SpecifiedUrl),
|
Url(SpecifiedUrl),
|
||||||
/// A `<gradient>` image.
|
/// A `<gradient>` image.
|
||||||
Gradient(G),
|
Gradient(Gradient),
|
||||||
/// A `-moz-image-rect` image
|
/// A `-moz-image-rect` image
|
||||||
Rect(ImageRect<N>),
|
Rect(ImageRect),
|
||||||
/// A `-moz-element(# <element-id>)`
|
/// A `-moz-element(# <element-id>)`
|
||||||
Element(Atom),
|
Element(Atom),
|
||||||
}
|
}
|
||||||
|
@ -34,11 +34,11 @@ pub enum Image<G, N> {
|
||||||
/// https://drafts.csswg.org/css-images/#gradients
|
/// https://drafts.csswg.org/css-images/#gradients
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct Gradient<K, C, L> {
|
pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color> {
|
||||||
/// Gradients can be linear or radial.
|
/// Gradients can be linear or radial.
|
||||||
pub kind: K,
|
pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position>,
|
||||||
/// The color stops and interpolation hints.
|
/// The color stops and interpolation hints.
|
||||||
pub items: Vec<GradientItem<C, L>>,
|
pub items: Vec<GradientItem<Color, LengthOrPercentage>>,
|
||||||
/// True if this is a repeating gradient.
|
/// True if this is a repeating gradient.
|
||||||
pub repeating: bool,
|
pub repeating: bool,
|
||||||
/// Compatibility mode.
|
/// Compatibility mode.
|
||||||
|
@ -55,26 +55,76 @@ pub enum CompatMode {
|
||||||
WebKit,
|
WebKit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A gradient kind.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position> {
|
||||||
|
/// A linear gradient.
|
||||||
|
Linear(LineDirection),
|
||||||
|
/// A radial gradient.
|
||||||
|
Radial(EndingShape<Length, LengthOrPercentage>, Position),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A radial gradient's ending shape.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub enum EndingShape<Length, LengthOrPercentage> {
|
||||||
|
/// A circular gradient.
|
||||||
|
Circle(Circle<Length>),
|
||||||
|
/// An elliptic gradient.
|
||||||
|
Ellipse(Ellipse<LengthOrPercentage>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A circle shape.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub enum Circle<Length> {
|
||||||
|
/// A circle radius.
|
||||||
|
Radius(Length),
|
||||||
|
/// A circle extent.
|
||||||
|
Extent(ShapeExtent),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An ellipse shape.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub enum Ellipse<LengthOrPercentage> {
|
||||||
|
/// An ellipse pair of radii.
|
||||||
|
Radii(LengthOrPercentage, LengthOrPercentage),
|
||||||
|
/// An ellipse extent.
|
||||||
|
Extent(ShapeExtent),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-images/#typedef-extent-keyword
|
||||||
|
define_css_keyword_enum!(ShapeExtent:
|
||||||
|
"closest-side" => ClosestSide,
|
||||||
|
"farthest-side" => FarthestSide,
|
||||||
|
"closest-corner" => ClosestCorner,
|
||||||
|
"farthest-corner" => FarthestCorner,
|
||||||
|
"contain" => Contain,
|
||||||
|
"cover" => Cover
|
||||||
|
);
|
||||||
|
|
||||||
/// A gradient item.
|
/// A gradient item.
|
||||||
/// https://drafts.csswg.org/css-images-4/#color-stop-syntax
|
/// https://drafts.csswg.org/css-images-4/#color-stop-syntax
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub enum GradientItem<C, L> {
|
pub enum GradientItem<Color, LengthOrPercentage> {
|
||||||
/// A color stop.
|
/// A color stop.
|
||||||
ColorStop(ColorStop<C, L>),
|
ColorStop(ColorStop<Color, LengthOrPercentage>),
|
||||||
/// An interpolation hint.
|
/// An interpolation hint.
|
||||||
InterpolationHint(L),
|
InterpolationHint(LengthOrPercentage),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A color stop.
|
/// A color stop.
|
||||||
/// https://drafts.csswg.org/css-images/#typedef-color-stop-list
|
/// https://drafts.csswg.org/css-images/#typedef-color-stop-list
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct ColorStop<C, L> {
|
pub struct ColorStop<Color, LengthOrPercentage> {
|
||||||
/// The color of this stop.
|
/// The color of this stop.
|
||||||
pub color: C,
|
pub color: Color,
|
||||||
/// The position of this stop.
|
/// The position of this stop.
|
||||||
pub position: Option<L>,
|
pub position: Option<LengthOrPercentage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Values for `moz-image-rect`.
|
/// Values for `moz-image-rect`.
|
||||||
|
@ -83,16 +133,16 @@ pub struct ColorStop<C, L> {
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct ImageRect<C> {
|
pub struct ImageRect<NumberOrPercentage> {
|
||||||
pub url: SpecifiedUrl,
|
pub url: SpecifiedUrl,
|
||||||
pub top: C,
|
pub top: NumberOrPercentage,
|
||||||
pub bottom: C,
|
pub bottom: NumberOrPercentage,
|
||||||
pub right: C,
|
pub right: NumberOrPercentage,
|
||||||
pub left: C,
|
pub left: NumberOrPercentage,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G, N> fmt::Debug for Image<G, N>
|
impl<G, R> fmt::Debug for Image<G, R>
|
||||||
where G: fmt::Debug, N: fmt::Debug,
|
where G: fmt::Debug, R: fmt::Debug,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -108,8 +158,8 @@ impl<G, N> fmt::Debug for Image<G, N>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G, N> ToCss for Image<G, N>
|
impl<G, R> ToCss for Image<G, R>
|
||||||
where G: ToCss, N: ToCss,
|
where G: ToCss, R: ToCss,
|
||||||
{
|
{
|
||||||
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 {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -125,7 +175,7 @@ impl<G, N> ToCss for Image<G, N>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G, N> HasViewportPercentage for Image<G, N>
|
impl<G, R> HasViewportPercentage for Image<G, R>
|
||||||
where G: HasViewportPercentage
|
where G: HasViewportPercentage
|
||||||
{
|
{
|
||||||
fn has_viewport_percentage(&self) -> bool {
|
fn has_viewport_percentage(&self) -> bool {
|
||||||
|
@ -136,11 +186,11 @@ impl<G, N> HasViewportPercentage for Image<G, N>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G, N> ToComputedValue for Image<G, N>
|
impl<G, R> ToComputedValue for Image<G, R>
|
||||||
where G: ToComputedValue, N: ToComputedValue,
|
where G: ToComputedValue, R: ToComputedValue,
|
||||||
{
|
{
|
||||||
type ComputedValue = Image<<G as ToComputedValue>::ComputedValue,
|
type ComputedValue = Image<<G as ToComputedValue>::ComputedValue,
|
||||||
<N as ToComputedValue>::ComputedValue>;
|
<R as ToComputedValue>::ComputedValue>;
|
||||||
|
|
||||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -177,8 +227,64 @@ impl<G, N> ToComputedValue for Image<G, N>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, C, L> HasViewportPercentage for Gradient<K, C, L>
|
impl<D, L, LoP, P, C> ToCss for Gradient<D, L, LoP, P, C>
|
||||||
where K: HasViewportPercentage, L: HasViewportPercentage,
|
where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss,
|
||||||
|
{
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
if self.compat_mode == CompatMode::WebKit {
|
||||||
|
dest.write_str("-webkit-")?;
|
||||||
|
}
|
||||||
|
if self.repeating {
|
||||||
|
dest.write_str("repeating-")?;
|
||||||
|
}
|
||||||
|
dest.write_str(self.kind.label())?;
|
||||||
|
dest.write_str("-gradient(")?;
|
||||||
|
let mut skip_comma = match self.kind {
|
||||||
|
GradientKind::Linear(ref direction) if direction.points_downwards() => true,
|
||||||
|
GradientKind::Linear(ref direction) => {
|
||||||
|
direction.to_css(dest, self.compat_mode)?;
|
||||||
|
false
|
||||||
|
},
|
||||||
|
GradientKind::Radial(ref shape, ref position) => {
|
||||||
|
let omit_shape = match *shape {
|
||||||
|
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
|
||||||
|
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => {
|
||||||
|
true
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if self.compat_mode == CompatMode::Modern {
|
||||||
|
if !omit_shape {
|
||||||
|
shape.to_css(dest)?;
|
||||||
|
dest.write_str(" ")?;
|
||||||
|
}
|
||||||
|
dest.write_str("at ")?;
|
||||||
|
position.to_css(dest)?;
|
||||||
|
} else {
|
||||||
|
position.to_css(dest)?;
|
||||||
|
if !omit_shape {
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
shape.to_css(dest)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
},
|
||||||
|
};
|
||||||
|
for item in &self.items {
|
||||||
|
if !skip_comma {
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
}
|
||||||
|
skip_comma = false;
|
||||||
|
item.to_css(dest)?;
|
||||||
|
}
|
||||||
|
dest.write_str(")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D, L, LoP, P, C> HasViewportPercentage for Gradient<D, L, LoP, P, C>
|
||||||
|
where L: HasViewportPercentage,
|
||||||
|
LoP: HasViewportPercentage,
|
||||||
|
P: HasViewportPercentage,
|
||||||
{
|
{
|
||||||
fn has_viewport_percentage(&self) -> bool {
|
fn has_viewport_percentage(&self) -> bool {
|
||||||
self.kind.has_viewport_percentage() ||
|
self.kind.has_viewport_percentage() ||
|
||||||
|
@ -186,12 +292,18 @@ impl<K, C, L> HasViewportPercentage for Gradient<K, C, L>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, C, L> ToComputedValue for Gradient<K, C, L>
|
impl<D, L, LoP, P, C> ToComputedValue for Gradient<D, L, LoP, P, C>
|
||||||
where K: ToComputedValue, C: ToComputedValue, L: ToComputedValue,
|
where D: ToComputedValue,
|
||||||
|
L: ToComputedValue,
|
||||||
|
LoP: ToComputedValue,
|
||||||
|
P: ToComputedValue,
|
||||||
|
C: ToComputedValue,
|
||||||
{
|
{
|
||||||
type ComputedValue = Gradient<<K as ToComputedValue>::ComputedValue,
|
type ComputedValue = Gradient<<D as ToComputedValue>::ComputedValue,
|
||||||
<C as ToComputedValue>::ComputedValue,
|
<L as ToComputedValue>::ComputedValue,
|
||||||
<L as ToComputedValue>::ComputedValue>;
|
<LoP as ToComputedValue>::ComputedValue,
|
||||||
|
<P as ToComputedValue>::ComputedValue,
|
||||||
|
<C as ToComputedValue>::ComputedValue>;
|
||||||
|
|
||||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
Gradient {
|
Gradient {
|
||||||
|
@ -212,6 +324,168 @@ impl<K, C, L> ToComputedValue for Gradient<K, C, L>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<D, L, LoP, P> GradientKind<D, L, LoP, P> {
|
||||||
|
fn label(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
GradientKind::Linear(..) => "linear",
|
||||||
|
GradientKind::Radial(..) => "radial",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D, L, LoP, P> HasViewportPercentage for GradientKind<D, L, LoP, P>
|
||||||
|
where L: HasViewportPercentage,
|
||||||
|
LoP: HasViewportPercentage,
|
||||||
|
P: HasViewportPercentage
|
||||||
|
{
|
||||||
|
fn has_viewport_percentage(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
GradientKind::Linear(_) => false,
|
||||||
|
GradientKind::Radial(ref shape, ref position) => {
|
||||||
|
shape.has_viewport_percentage() || position.has_viewport_percentage()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D, L, LoP, P> ToComputedValue for GradientKind<D, L, LoP, P>
|
||||||
|
where D: ToComputedValue,
|
||||||
|
L: ToComputedValue,
|
||||||
|
LoP: ToComputedValue,
|
||||||
|
P: ToComputedValue,
|
||||||
|
{
|
||||||
|
type ComputedValue = GradientKind<<D as ToComputedValue>::ComputedValue,
|
||||||
|
<L as ToComputedValue>::ComputedValue,
|
||||||
|
<LoP as ToComputedValue>::ComputedValue,
|
||||||
|
<P as ToComputedValue>::ComputedValue>;
|
||||||
|
|
||||||
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
|
match *self {
|
||||||
|
GradientKind::Linear(ref direction) => {
|
||||||
|
GradientKind::Linear(direction.to_computed_value(context))
|
||||||
|
},
|
||||||
|
GradientKind::Radial(ref shape, ref position) => {
|
||||||
|
GradientKind::Radial(shape.to_computed_value(context), position.to_computed_value(context))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||||
|
match *computed {
|
||||||
|
GradientKind::Linear(ref direction) => {
|
||||||
|
GradientKind::Linear(ToComputedValue::from_computed_value(direction))
|
||||||
|
},
|
||||||
|
GradientKind::Radial(ref shape, ref position) => {
|
||||||
|
GradientKind::Radial(
|
||||||
|
ToComputedValue::from_computed_value(shape),
|
||||||
|
ToComputedValue::from_computed_value(position),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The direction of a linear gradient.
|
||||||
|
pub trait LineDirection {
|
||||||
|
/// Whether this direction points towards, and thus can be omitted.
|
||||||
|
fn points_downwards(&self) -> bool;
|
||||||
|
|
||||||
|
/// Serialises this direction according to the compatibility mode.
|
||||||
|
fn to_css<W>(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result
|
||||||
|
where W: fmt::Write;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, LoP> ToCss for EndingShape<L, LoP>
|
||||||
|
where L: ToCss, LoP: ToCss,
|
||||||
|
{
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
match *self {
|
||||||
|
EndingShape::Circle(Circle::Extent(ShapeExtent::FarthestCorner)) |
|
||||||
|
EndingShape::Circle(Circle::Extent(ShapeExtent::Cover)) => {
|
||||||
|
dest.write_str("circle")
|
||||||
|
},
|
||||||
|
EndingShape::Circle(Circle::Extent(keyword)) => {
|
||||||
|
dest.write_str("circle ")?;
|
||||||
|
keyword.to_css(dest)
|
||||||
|
},
|
||||||
|
EndingShape::Circle(Circle::Radius(ref length)) => {
|
||||||
|
length.to_css(dest)
|
||||||
|
},
|
||||||
|
EndingShape::Ellipse(Ellipse::Extent(keyword)) => {
|
||||||
|
keyword.to_css(dest)
|
||||||
|
},
|
||||||
|
EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => {
|
||||||
|
x.to_css(dest)?;
|
||||||
|
dest.write_str(" ")?;
|
||||||
|
y.to_css(dest)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, LoP> HasViewportPercentage for EndingShape<L, LoP>
|
||||||
|
where L: HasViewportPercentage, LoP: HasViewportPercentage,
|
||||||
|
{
|
||||||
|
fn has_viewport_percentage(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
EndingShape::Circle(Circle::Radius(ref length)) => {
|
||||||
|
length.has_viewport_percentage()
|
||||||
|
},
|
||||||
|
EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => {
|
||||||
|
x.has_viewport_percentage() || y.has_viewport_percentage()
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, LoP> ToComputedValue for EndingShape<L, LoP>
|
||||||
|
where L: ToComputedValue, LoP: ToComputedValue,
|
||||||
|
{
|
||||||
|
type ComputedValue = EndingShape<<L as ToComputedValue>::ComputedValue,
|
||||||
|
<LoP as ToComputedValue>::ComputedValue>;
|
||||||
|
|
||||||
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
|
match *self {
|
||||||
|
EndingShape::Circle(Circle::Radius(ref length)) => {
|
||||||
|
EndingShape::Circle(Circle::Radius(length.to_computed_value(context)))
|
||||||
|
},
|
||||||
|
EndingShape::Circle(Circle::Extent(extent)) => {
|
||||||
|
EndingShape::Circle(Circle::Extent(extent))
|
||||||
|
},
|
||||||
|
EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => {
|
||||||
|
EndingShape::Ellipse(Ellipse::Radii(
|
||||||
|
x.to_computed_value(context),
|
||||||
|
y.to_computed_value(context),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
EndingShape::Ellipse(Ellipse::Extent(extent)) => {
|
||||||
|
EndingShape::Ellipse(Ellipse::Extent(extent))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||||
|
match *computed {
|
||||||
|
EndingShape::Circle(Circle::Radius(ref length)) => {
|
||||||
|
EndingShape::Circle(Circle::Radius(ToComputedValue::from_computed_value(length)))
|
||||||
|
},
|
||||||
|
EndingShape::Circle(Circle::Extent(extent)) => {
|
||||||
|
EndingShape::Circle(Circle::Extent(extent))
|
||||||
|
},
|
||||||
|
EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => {
|
||||||
|
EndingShape::Ellipse(Ellipse::Radii(
|
||||||
|
ToComputedValue::from_computed_value(x),
|
||||||
|
ToComputedValue::from_computed_value(y),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
EndingShape::Ellipse(Ellipse::Extent(extent)) => {
|
||||||
|
EndingShape::Ellipse(Ellipse::Extent(extent))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<C, L> ToCss for GradientItem<C, L>
|
impl<C, L> ToCss for GradientItem<C, L>
|
||||||
where C: ToCss, L: ToCss,
|
where C: ToCss, L: ToCss,
|
||||||
{
|
{
|
||||||
|
@ -342,7 +616,7 @@ impl<C> ToComputedValue for ImageRect<C>
|
||||||
|
|
||||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
ImageRect {
|
ImageRect {
|
||||||
url: self.url.clone(),
|
url: self.url.to_computed_value(context),
|
||||||
top: self.top.to_computed_value(context),
|
top: self.top.to_computed_value(context),
|
||||||
right: self.right.to_computed_value(context),
|
right: self.right.to_computed_value(context),
|
||||||
bottom: self.bottom.to_computed_value(context),
|
bottom: self.bottom.to_computed_value(context),
|
||||||
|
@ -352,7 +626,7 @@ impl<C> ToComputedValue for ImageRect<C>
|
||||||
|
|
||||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||||
ImageRect {
|
ImageRect {
|
||||||
url: computed.url.clone(),
|
url: ToComputedValue::from_computed_value(&computed.url),
|
||||||
top: ToComputedValue::from_computed_value(&computed.top),
|
top: ToComputedValue::from_computed_value(&computed.top),
|
||||||
right: ToComputedValue::from_computed_value(&computed.right),
|
right: ToComputedValue::from_computed_value(&computed.right),
|
||||||
bottom: ToComputedValue::from_computed_value(&computed.bottom),
|
bottom: ToComputedValue::from_computed_value(&computed.bottom),
|
||||||
|
|
|
@ -12,22 +12,56 @@ use cssparser::{Parser, Token};
|
||||||
use parser::{Parse, ParserContext};
|
use parser::{Parse, ParserContext};
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
use std::f32::consts::PI;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use values::generics::image::{CompatMode, ColorStop as GenericColorStop};
|
use values::generics::image::{Circle, CompatMode, Ellipse, ColorStop as GenericColorStop};
|
||||||
use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem};
|
use values::generics::image::{EndingShape as GenericEndingShape, Gradient as GenericGradient};
|
||||||
|
use values::generics::image::{GradientItem as GenericGradientItem, GradientKind as GenericGradientKind};
|
||||||
use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect};
|
use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect};
|
||||||
use values::specified::{Angle, CSSColor, Length, LengthOrPercentage, NumberOrPercentage};
|
use values::generics::image::{LineDirection as GenericsLineDirection, ShapeExtent};
|
||||||
|
use values::specified::{Angle, CSSColor, Length, LengthOrPercentage, NumberOrPercentage, Percentage};
|
||||||
use values::specified::position::{Position, X, Y};
|
use values::specified::position::{Position, X, Y};
|
||||||
use values::specified::url::SpecifiedUrl;
|
use values::specified::url::SpecifiedUrl;
|
||||||
|
|
||||||
/// Specified values for an image according to CSS-IMAGES.
|
/// Specified values for an image according to CSS-IMAGES.
|
||||||
/// https://drafts.csswg.org/css-images/#image-values
|
/// https://drafts.csswg.org/css-images/#image-values
|
||||||
pub type Image = GenericImage<Gradient, NumberOrPercentage>;
|
pub type Image = GenericImage<Gradient, ImageRect>;
|
||||||
|
|
||||||
/// Specified values for a CSS gradient.
|
/// Specified values for a CSS gradient.
|
||||||
/// https://drafts.csswg.org/css-images/#gradients
|
/// https://drafts.csswg.org/css-images/#gradients
|
||||||
pub type Gradient = GenericGradient<GradientKind, CSSColor, LengthOrPercentage>;
|
pub type Gradient = GenericGradient<
|
||||||
|
LineDirection,
|
||||||
|
Length,
|
||||||
|
LengthOrPercentage,
|
||||||
|
Position,
|
||||||
|
CSSColor,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// A specified gradient kind.
|
||||||
|
pub type GradientKind = GenericGradientKind<
|
||||||
|
LineDirection,
|
||||||
|
Length,
|
||||||
|
LengthOrPercentage,
|
||||||
|
Position,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// A specified gradient line direction.
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub enum LineDirection {
|
||||||
|
/// An angular direction.
|
||||||
|
Angle(Angle),
|
||||||
|
/// A horizontal direction.
|
||||||
|
Horizontal(X),
|
||||||
|
/// A vertical direction.
|
||||||
|
Vertical(Y),
|
||||||
|
/// A direction towards a corner of a box.
|
||||||
|
Corner(X, Y),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A specified ending shape.
|
||||||
|
pub type EndingShape = GenericEndingShape<Length, LengthOrPercentage>;
|
||||||
|
|
||||||
/// A specified gradient item.
|
/// A specified gradient item.
|
||||||
pub type GradientItem = GenericGradientItem<CSSColor, LengthOrPercentage>;
|
pub type GradientItem = GenericGradientItem<CSSColor, LengthOrPercentage>;
|
||||||
|
@ -77,103 +111,59 @@ impl Image {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for Gradient {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
if self.compat_mode == CompatMode::WebKit {
|
|
||||||
try!(dest.write_str("-webkit-"));
|
|
||||||
}
|
|
||||||
if self.repeating {
|
|
||||||
try!(dest.write_str("repeating-"));
|
|
||||||
}
|
|
||||||
let mut skipcomma = false;
|
|
||||||
match self.kind {
|
|
||||||
GradientKind::Linear(angle_or_corner) => {
|
|
||||||
try!(dest.write_str("linear-gradient("));
|
|
||||||
try!(angle_or_corner.to_css(dest, self.compat_mode));
|
|
||||||
if angle_or_corner == AngleOrCorner::None {
|
|
||||||
skipcomma = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
GradientKind::Radial(ref shape, ref position) => {
|
|
||||||
try!(dest.write_str("radial-gradient("));
|
|
||||||
if self.compat_mode == CompatMode::Modern {
|
|
||||||
try!(shape.to_css(dest));
|
|
||||||
try!(dest.write_str(" at "));
|
|
||||||
try!(position.to_css(dest));
|
|
||||||
} else {
|
|
||||||
try!(position.to_css(dest));
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
try!(shape.to_css(dest));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for item in &self.items {
|
|
||||||
if !skipcomma {
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
} else {
|
|
||||||
skipcomma = false;
|
|
||||||
}
|
|
||||||
try!(item.to_css(dest));
|
|
||||||
}
|
|
||||||
dest.write_str(")")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gradient {
|
impl Gradient {
|
||||||
/// Parses a gradient from the given arguments.
|
/// Parses a gradient from the given arguments.
|
||||||
pub fn parse_function(context: &ParserContext, input: &mut Parser) -> Result<Gradient, ()> {
|
pub fn parse_function(context: &ParserContext, input: &mut Parser) -> Result<Gradient, ()> {
|
||||||
fn parse<F>(context: &ParserContext, input: &mut Parser, parse_kind: F)
|
enum Shape {
|
||||||
-> Result<(GradientKind, Vec<GradientItem>), ()>
|
Linear,
|
||||||
where F: FnOnce(&ParserContext, &mut Parser) -> Result<GradientKind, ()>
|
Radial,
|
||||||
{
|
}
|
||||||
input.parse_nested_block(|input| {
|
|
||||||
let kind = try!(parse_kind(context, input));
|
let (shape, repeating, compat_mode) = match_ignore_ascii_case! { &try!(input.expect_function()),
|
||||||
let items = try!(Gradient::parse_items(context, input));
|
|
||||||
Ok((kind, items))
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let mut repeating = false;
|
|
||||||
let mut compat_mode = CompatMode::Modern;
|
|
||||||
let (gradient_kind, items) = match_ignore_ascii_case! { &try!(input.expect_function()),
|
|
||||||
"linear-gradient" => {
|
"linear-gradient" => {
|
||||||
try!(parse(context, input, GradientKind::parse_modern_linear))
|
(Shape::Linear, false, CompatMode::Modern)
|
||||||
},
|
},
|
||||||
"-webkit-linear-gradient" => {
|
"-webkit-linear-gradient" => {
|
||||||
compat_mode = CompatMode::WebKit;
|
(Shape::Linear, false, CompatMode::WebKit)
|
||||||
try!(parse(context, input, GradientKind::parse_webkit_linear))
|
|
||||||
},
|
},
|
||||||
"repeating-linear-gradient" => {
|
"repeating-linear-gradient" => {
|
||||||
repeating = true;
|
(Shape::Linear, true, CompatMode::Modern)
|
||||||
try!(parse(context, input, GradientKind::parse_modern_linear))
|
|
||||||
},
|
},
|
||||||
"-webkit-repeating-linear-gradient" => {
|
"-webkit-repeating-linear-gradient" => {
|
||||||
repeating = true;
|
(Shape::Linear, true, CompatMode::WebKit)
|
||||||
compat_mode = CompatMode::WebKit;
|
|
||||||
try!(parse(context, input, GradientKind::parse_webkit_linear))
|
|
||||||
},
|
},
|
||||||
"radial-gradient" => {
|
"radial-gradient" => {
|
||||||
try!(parse(context, input, GradientKind::parse_modern_radial))
|
(Shape::Radial, false, CompatMode::Modern)
|
||||||
},
|
},
|
||||||
"-webkit-radial-gradient" => {
|
"-webkit-radial-gradient" => {
|
||||||
compat_mode = CompatMode::WebKit;
|
(Shape::Radial, false, CompatMode::WebKit)
|
||||||
try!(parse(context, input, GradientKind::parse_webkit_radial))
|
|
||||||
},
|
},
|
||||||
"repeating-radial-gradient" => {
|
"repeating-radial-gradient" => {
|
||||||
repeating = true;
|
(Shape::Radial, true, CompatMode::Modern)
|
||||||
try!(parse(context, input, GradientKind::parse_modern_radial))
|
|
||||||
},
|
},
|
||||||
"-webkit-repeating-radial-gradient" => {
|
"-webkit-repeating-radial-gradient" => {
|
||||||
repeating = true;
|
(Shape::Radial, true, CompatMode::WebKit)
|
||||||
compat_mode = CompatMode::WebKit;
|
|
||||||
try!(parse(context, input, GradientKind::parse_webkit_radial))
|
|
||||||
},
|
},
|
||||||
_ => { return Err(()); }
|
_ => { return Err(()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (kind, items) = input.parse_nested_block(|i| {
|
||||||
|
let shape = match shape {
|
||||||
|
Shape::Linear => GradientKind::parse_linear(context, i, compat_mode)?,
|
||||||
|
Shape::Radial => GradientKind::parse_radial(context, i, compat_mode)?,
|
||||||
|
};
|
||||||
|
let items = Gradient::parse_items(context, i)?;
|
||||||
|
Ok((shape, items))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if items.len() < 2 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Gradient {
|
Ok(Gradient {
|
||||||
items: items,
|
items: items,
|
||||||
repeating: repeating,
|
repeating: repeating,
|
||||||
kind: gradient_kind,
|
kind: kind,
|
||||||
compat_mode: compat_mode,
|
compat_mode: compat_mode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -197,167 +187,205 @@ impl Gradient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specified values for CSS linear or radial gradients.
|
|
||||||
/// https://drafts.csswg.org/css-images/#gradients
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub enum GradientKind {
|
|
||||||
/// A `<linear-gradient()>`:
|
|
||||||
///
|
|
||||||
/// https://drafts.csswg.org/css-images/#funcdef-linear-gradient
|
|
||||||
Linear(AngleOrCorner),
|
|
||||||
|
|
||||||
/// A `<radial-gradient()>`:
|
|
||||||
///
|
|
||||||
/// https://drafts.csswg.org/css-images/#radial-gradients
|
|
||||||
Radial(EndingShape, Position),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GradientKind {
|
impl GradientKind {
|
||||||
/// Parses a linear gradient kind from the given arguments.
|
fn parse_linear(context: &ParserContext,
|
||||||
fn parse_modern_linear(context: &ParserContext, input: &mut Parser) -> Result<GradientKind, ()> {
|
input: &mut Parser,
|
||||||
let direction = if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) {
|
compat_mode: CompatMode)
|
||||||
try!(input.expect_comma());
|
-> Result<Self, ()> {
|
||||||
AngleOrCorner::Angle(angle)
|
let direction = if let Ok(d) = input.try(|i| LineDirection::parse(context, i, compat_mode)) {
|
||||||
|
input.expect_comma()?;
|
||||||
|
d
|
||||||
} else {
|
} else {
|
||||||
if input.try(|i| i.expect_ident_matching("to")).is_ok() {
|
LineDirection::Vertical(Y::Bottom)
|
||||||
let (horizontal, vertical) =
|
|
||||||
if let Ok(value) = input.try(X::parse) {
|
|
||||||
(Some(value), input.try(Y::parse).ok())
|
|
||||||
} else {
|
|
||||||
let value = try!(Y::parse(input));
|
|
||||||
(input.try(X::parse).ok(), Some(value))
|
|
||||||
};
|
};
|
||||||
try!(input.expect_comma());
|
Ok(GenericGradientKind::Linear(direction))
|
||||||
AngleOrCorner::Corner(horizontal, vertical)
|
|
||||||
} else {
|
|
||||||
AngleOrCorner::None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(GradientKind::Linear(direction))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_webkit_linear(context: &ParserContext, input: &mut Parser) -> Result<GradientKind, ()> {
|
fn parse_radial(context: &ParserContext,
|
||||||
let direction = if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) {
|
input: &mut Parser,
|
||||||
AngleOrCorner::Angle(angle)
|
compat_mode: CompatMode)
|
||||||
|
-> Result<Self, ()> {
|
||||||
|
let (shape, position) = if compat_mode == CompatMode::Modern {
|
||||||
|
let shape = input.try(|i| EndingShape::parse(context, i, compat_mode));
|
||||||
|
let position = input.try(|i| {
|
||||||
|
i.expect_ident_matching("at")?;
|
||||||
|
Position::parse(context, i)
|
||||||
|
});
|
||||||
|
(shape, position)
|
||||||
} else {
|
} else {
|
||||||
if let Ok(value) = input.try(X::parse) {
|
let position = input.try(|i| Position::parse(context, i));
|
||||||
AngleOrCorner::Corner(Some(value), input.try(Y::parse).ok())
|
let shape = input.try(|i| {
|
||||||
} else {
|
if position.is_ok() {
|
||||||
if let Ok(value) = input.try(Y::parse) {
|
i.expect_comma()?;
|
||||||
AngleOrCorner::Corner(input.try(X::parse).ok(), Some(value))
|
|
||||||
} else {
|
|
||||||
AngleOrCorner::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if direction != AngleOrCorner::None {
|
|
||||||
try!(input.expect_comma());
|
|
||||||
}
|
|
||||||
Ok(GradientKind::Linear(direction))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses a modern radial gradient from the given arguments.
|
|
||||||
pub fn parse_modern_radial(context: &ParserContext, input: &mut Parser) -> Result<GradientKind, ()> {
|
|
||||||
let mut needs_comma = true;
|
|
||||||
|
|
||||||
// Ending shape and position can be in various order. Checks all probabilities.
|
|
||||||
let (shape, position) = if let Ok(position) = input.try(|i| parse_position(context, i)) {
|
|
||||||
// Handle just <position>
|
|
||||||
(EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), position)
|
|
||||||
} else if let Ok((first, second)) = input.try(|i| parse_two_length(context, i)) {
|
|
||||||
// Handle <LengthOrPercentage> <LengthOrPercentage> <shape>? <position>?
|
|
||||||
let _ = input.try(|input| input.expect_ident_matching("ellipse"));
|
|
||||||
(EndingShape::Ellipse(LengthOrPercentageOrKeyword::LengthOrPercentage(first, second)),
|
|
||||||
input.try(|i| parse_position(context, i)).unwrap_or(Position::center()))
|
|
||||||
} else if let Ok(length) = input.try(|i| Length::parse(context, i)) {
|
|
||||||
// Handle <Length> <circle>? <position>?
|
|
||||||
let _ = input.try(|input| input.expect_ident_matching("circle"));
|
|
||||||
(EndingShape::Circle(LengthOrKeyword::Length(length)),
|
|
||||||
input.try(|i| parse_position(context, i)).unwrap_or(Position::center()))
|
|
||||||
} else if let Ok(keyword) = input.try(SizeKeyword::parse_modern) {
|
|
||||||
// Handle <keyword> <shape-keyword>? <position>?
|
|
||||||
let shape = if input.try(|input| input.expect_ident_matching("circle")).is_ok() {
|
|
||||||
EndingShape::Circle(LengthOrKeyword::Keyword(keyword))
|
|
||||||
} else {
|
|
||||||
let _ = input.try(|input| input.expect_ident_matching("ellipse"));
|
|
||||||
EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword))
|
|
||||||
};
|
|
||||||
(shape, input.try(|i| parse_position(context, i)).unwrap_or(Position::center()))
|
|
||||||
} else {
|
|
||||||
// Handle <shape-keyword> <length>? <position>?
|
|
||||||
if input.try(|input| input.expect_ident_matching("ellipse")).is_ok() {
|
|
||||||
// Handle <ellipse> <LengthOrPercentageOrKeyword>? <position>?
|
|
||||||
let length = input.try(|i| LengthOrPercentageOrKeyword::parse(context, i, SizeKeyword::parse_modern))
|
|
||||||
.unwrap_or(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner));
|
|
||||||
(EndingShape::Ellipse(length),
|
|
||||||
input.try(|i| parse_position(context, i)).unwrap_or(Position::center()))
|
|
||||||
} else if input.try(|input| input.expect_ident_matching("circle")).is_ok() {
|
|
||||||
// Handle <ellipse> <LengthOrKeyword>? <position>?
|
|
||||||
let length = input.try(|i| LengthOrKeyword::parse(context, i, SizeKeyword::parse_modern))
|
|
||||||
.unwrap_or(LengthOrKeyword::Keyword(SizeKeyword::FarthestCorner));
|
|
||||||
(EndingShape::Circle(length), input.try(|i| parse_position(context, i))
|
|
||||||
.unwrap_or(Position::center()))
|
|
||||||
} else {
|
|
||||||
// If there is no shape keyword, it should set to default.
|
|
||||||
needs_comma = false;
|
|
||||||
(EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)),
|
|
||||||
input.try(|i| parse_position(context, i)).unwrap_or(Position::center()))
|
|
||||||
}
|
}
|
||||||
|
EndingShape::parse(context, i, compat_mode)
|
||||||
|
});
|
||||||
|
(shape, position)
|
||||||
};
|
};
|
||||||
|
|
||||||
if needs_comma {
|
if shape.is_ok() || position.is_ok() {
|
||||||
try!(input.expect_comma());
|
input.expect_comma()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(GradientKind::Radial(shape, position))
|
let shape = shape.unwrap_or({
|
||||||
|
GenericEndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner))
|
||||||
|
});
|
||||||
|
let position = position.unwrap_or(Position::center());
|
||||||
|
Ok(GenericGradientKind::Radial(shape, position))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses a webkit radial gradient from the given arguments.
|
impl GenericsLineDirection for LineDirection {
|
||||||
/// https://compat.spec.whatwg.org/#css-gradients-webkit-radial-gradient
|
fn points_downwards(&self) -> bool {
|
||||||
pub fn parse_webkit_radial(context: &ParserContext, input: &mut Parser) -> Result<GradientKind, ()> {
|
match *self {
|
||||||
let position = if let Ok(position) = input.try(|i| Position::parse(context, i)) {
|
LineDirection::Angle(ref angle) => angle.radians() == PI,
|
||||||
try!(input.expect_comma());
|
LineDirection::Vertical(Y::Bottom) => true,
|
||||||
position
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_css<W>(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result
|
||||||
|
where W: fmt::Write
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
LineDirection::Angle(angle) => {
|
||||||
|
angle.to_css(dest)
|
||||||
|
},
|
||||||
|
LineDirection::Horizontal(x) => {
|
||||||
|
if compat_mode == CompatMode::Modern {
|
||||||
|
dest.write_str("to ")?;
|
||||||
|
}
|
||||||
|
x.to_css(dest)
|
||||||
|
},
|
||||||
|
LineDirection::Vertical(y) => {
|
||||||
|
if compat_mode == CompatMode::Modern {
|
||||||
|
dest.write_str("to ")?;
|
||||||
|
}
|
||||||
|
y.to_css(dest)
|
||||||
|
},
|
||||||
|
LineDirection::Corner(x, y) => {
|
||||||
|
if compat_mode == CompatMode::Modern {
|
||||||
|
dest.write_str("to ")?;
|
||||||
|
}
|
||||||
|
x.to_css(dest)?;
|
||||||
|
dest.write_str(" ")?;
|
||||||
|
y.to_css(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineDirection {
|
||||||
|
fn parse(context: &ParserContext,
|
||||||
|
input: &mut Parser,
|
||||||
|
compat_mode: CompatMode)
|
||||||
|
-> Result<Self, ()> {
|
||||||
|
if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) {
|
||||||
|
return Ok(LineDirection::Angle(angle));
|
||||||
|
}
|
||||||
|
input.try(|i| {
|
||||||
|
if compat_mode == CompatMode::Modern {
|
||||||
|
i.expect_ident_matching("to")?;
|
||||||
|
}
|
||||||
|
if let Ok(x) = i.try(X::parse) {
|
||||||
|
if let Ok(y) = i.try(Y::parse) {
|
||||||
|
return Ok(LineDirection::Corner(x, y));
|
||||||
|
}
|
||||||
|
return Ok(LineDirection::Horizontal(x));
|
||||||
|
}
|
||||||
|
let y = Y::parse(i)?;
|
||||||
|
if let Ok(x) = i.try(X::parse) {
|
||||||
|
return Ok(LineDirection::Corner(x, y));
|
||||||
|
}
|
||||||
|
Ok(LineDirection::Vertical(y))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EndingShape {
|
||||||
|
fn parse(context: &ParserContext,
|
||||||
|
input: &mut Parser,
|
||||||
|
compat_mode: CompatMode)
|
||||||
|
-> Result<Self, ()> {
|
||||||
|
if let Ok(extent) = input.try(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode)) {
|
||||||
|
if input.try(|i| i.expect_ident_matching("circle")).is_ok() {
|
||||||
|
return Ok(GenericEndingShape::Circle(Circle::Extent(extent)));
|
||||||
|
}
|
||||||
|
let _ = input.try(|i| i.expect_ident_matching("ellipse"));
|
||||||
|
return Ok(GenericEndingShape::Ellipse(Ellipse::Extent(extent)));
|
||||||
|
}
|
||||||
|
if input.try(|i| i.expect_ident_matching("circle")).is_ok() {
|
||||||
|
if let Ok(extent) = input.try(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode)) {
|
||||||
|
return Ok(GenericEndingShape::Circle(Circle::Extent(extent)));
|
||||||
|
}
|
||||||
|
if compat_mode == CompatMode::Modern {
|
||||||
|
if let Ok(length) = input.try(|i| Length::parse(context, i)) {
|
||||||
|
return Ok(GenericEndingShape::Circle(Circle::Radius(length)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(GenericEndingShape::Circle(Circle::Extent(ShapeExtent::FarthestCorner)));
|
||||||
|
}
|
||||||
|
if input.try(|i| i.expect_ident_matching("ellipse")).is_ok() {
|
||||||
|
if let Ok(extent) = input.try(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode)) {
|
||||||
|
return Ok(GenericEndingShape::Ellipse(Ellipse::Extent(extent)));
|
||||||
|
}
|
||||||
|
if compat_mode == CompatMode::Modern {
|
||||||
|
let pair: Result<_, ()> = input.try(|i| {
|
||||||
|
let x = LengthOrPercentage::parse(context, i)?;
|
||||||
|
let y = LengthOrPercentage::parse(context, i)?;
|
||||||
|
Ok((x, y))
|
||||||
|
});
|
||||||
|
if let Ok((x, y)) = pair {
|
||||||
|
return Ok(GenericEndingShape::Ellipse(Ellipse::Radii(x, y)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(GenericEndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)));
|
||||||
|
}
|
||||||
|
if let Ok(length) = input.try(|i| Length::parse(context, i)) {
|
||||||
|
if let Ok(y) = input.try(|i| LengthOrPercentage::parse(context, i)) {
|
||||||
|
if compat_mode == CompatMode::Modern {
|
||||||
|
let _ = input.try(|i| i.expect_ident_matching("ellipse"));
|
||||||
|
}
|
||||||
|
return Ok(GenericEndingShape::Ellipse(Ellipse::Radii(length.into(), y)));
|
||||||
|
}
|
||||||
|
if compat_mode == CompatMode::Modern {
|
||||||
|
let y = input.try(|i| {
|
||||||
|
i.expect_ident_matching("ellipse")?;
|
||||||
|
LengthOrPercentage::parse(context, i)
|
||||||
|
});
|
||||||
|
if let Ok(y) = y {
|
||||||
|
return Ok(GenericEndingShape::Ellipse(Ellipse::Radii(length.into(), y)));
|
||||||
|
}
|
||||||
|
let _ = input.try(|i| i.expect_ident_matching("circle"));
|
||||||
|
}
|
||||||
|
return Ok(GenericEndingShape::Circle(Circle::Radius(length)));
|
||||||
|
}
|
||||||
|
input.try(|i| {
|
||||||
|
let x = Percentage::parse(context, i)?;
|
||||||
|
let y = if let Ok(y) = i.try(|i| LengthOrPercentage::parse(context, i)) {
|
||||||
|
if compat_mode == CompatMode::Modern {
|
||||||
|
let _ = i.try(|i| i.expect_ident_matching("ellipse"));
|
||||||
|
}
|
||||||
|
y
|
||||||
} else {
|
} else {
|
||||||
Position::center()
|
if compat_mode == CompatMode::Modern {
|
||||||
|
i.expect_ident_matching("ellipse")?;
|
||||||
|
}
|
||||||
|
LengthOrPercentage::parse(context, i)?
|
||||||
};
|
};
|
||||||
|
Ok(GenericEndingShape::Ellipse(Ellipse::Radii(x.into(), y)))
|
||||||
let mut needs_comma = true;
|
})
|
||||||
|
|
||||||
// Ending shape and position can be in various order. Checks all probabilities.
|
|
||||||
let shape = if let Ok((first, second)) = input.try(|i| parse_two_length(context, i)) {
|
|
||||||
EndingShape::Ellipse(LengthOrPercentageOrKeyword::LengthOrPercentage(first, second))
|
|
||||||
} else if let Ok(keyword) = input.try(SizeKeyword::parse) {
|
|
||||||
// Handle <keyword> <shape-keyword>?
|
|
||||||
if input.try(|input| input.expect_ident_matching("circle")).is_ok() {
|
|
||||||
EndingShape::Circle(LengthOrKeyword::Keyword(keyword))
|
|
||||||
} else {
|
|
||||||
let _ = input.try(|input| input.expect_ident_matching("ellipse"));
|
|
||||||
EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword))
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Handle <shape-keyword> <keyword>?
|
|
||||||
if input.try(|input| input.expect_ident_matching("ellipse")).is_ok() {
|
|
||||||
// Handle <ellipse> <keyword>?
|
|
||||||
let keyword = input.try(SizeKeyword::parse).unwrap_or((SizeKeyword::Cover));
|
|
||||||
EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword))
|
|
||||||
} else if input.try(|input| input.expect_ident_matching("circle")).is_ok() {
|
|
||||||
// Handle <circle> <keyword>?
|
|
||||||
let keyword = input.try(SizeKeyword::parse).unwrap_or((SizeKeyword::Cover));
|
|
||||||
EndingShape::Circle(LengthOrKeyword::Keyword(keyword))
|
|
||||||
} else {
|
|
||||||
// If there is no shape keyword, it should set to default.
|
|
||||||
needs_comma = false;
|
|
||||||
EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::Cover))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if needs_comma {
|
|
||||||
try!(input.expect_comma());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(GradientKind::Radial(shape, position))
|
impl ShapeExtent {
|
||||||
|
fn parse_with_compat_mode(input: &mut Parser,
|
||||||
|
compat_mode: CompatMode)
|
||||||
|
-> Result<Self, ()> {
|
||||||
|
match try!(Self::parse(input)) {
|
||||||
|
ShapeExtent::Contain | ShapeExtent::Cover if compat_mode == CompatMode::Modern => Err(()),
|
||||||
|
keyword => Ok(keyword),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,54 +418,6 @@ impl Parse for ImageRect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_two_length(context: &ParserContext, input: &mut Parser)
|
|
||||||
-> Result<(LengthOrPercentage, LengthOrPercentage), ()> {
|
|
||||||
let first = try!(LengthOrPercentage::parse(context, input));
|
|
||||||
let second = try!(LengthOrPercentage::parse(context, input));
|
|
||||||
Ok((first, second))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_position(context: &ParserContext, input: &mut Parser) -> Result<Position, ()> {
|
|
||||||
try!(input.expect_ident_matching("at"));
|
|
||||||
input.try(|i| Position::parse(context, i))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Specified values for an angle or a corner in a linear gradient.
|
|
||||||
#[derive(Clone, PartialEq, Copy, Debug)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum AngleOrCorner {
|
|
||||||
Angle(Angle),
|
|
||||||
Corner(Option<X>, Option<Y>),
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AngleOrCorner {
|
|
||||||
fn to_css<W>(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result where W: fmt::Write {
|
|
||||||
match *self {
|
|
||||||
AngleOrCorner::None => Ok(()),
|
|
||||||
AngleOrCorner::Angle(angle) => angle.to_css(dest),
|
|
||||||
AngleOrCorner::Corner(horizontal, vertical) => {
|
|
||||||
if compat_mode == CompatMode::Modern {
|
|
||||||
try!(dest.write_str("to "));
|
|
||||||
}
|
|
||||||
let mut horizontal_present = false;
|
|
||||||
if let Some(horizontal) = horizontal {
|
|
||||||
try!(horizontal.to_css(dest));
|
|
||||||
horizontal_present = true;
|
|
||||||
}
|
|
||||||
if let Some(vertical) = vertical {
|
|
||||||
if horizontal_present {
|
|
||||||
try!(dest.write_str(" "));
|
|
||||||
}
|
|
||||||
try!(vertical.to_css(dest));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for ColorStop {
|
impl Parse for ColorStop {
|
||||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
Ok(ColorStop {
|
Ok(ColorStop {
|
||||||
|
@ -447,115 +427,6 @@ impl Parse for ColorStop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether the gradient's ending shape is a circle or an ellipse.
|
|
||||||
/// If <shape> is omitted, the ending shape defaults to a circle
|
|
||||||
/// if the <size> is a single <length>, and to an ellipse otherwise.
|
|
||||||
/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-ending-shape
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum EndingShape {
|
|
||||||
Circle(LengthOrKeyword),
|
|
||||||
Ellipse(LengthOrPercentageOrKeyword),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for EndingShape {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
match *self {
|
|
||||||
EndingShape::Circle(ref length) => {
|
|
||||||
try!(dest.write_str("circle "));
|
|
||||||
try!(length.to_css(dest));
|
|
||||||
},
|
|
||||||
EndingShape::Ellipse(ref length) => {
|
|
||||||
try!(dest.write_str("ellipse "));
|
|
||||||
try!(length.to_css(dest));
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum LengthOrKeyword {
|
|
||||||
Length(Length),
|
|
||||||
Keyword(SizeKeyword),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LengthOrKeyword {
|
|
||||||
fn parse<F>(context: &ParserContext, input: &mut Parser, parse_size_keyword: F) -> Result<Self, ()>
|
|
||||||
where F: Fn(&mut Parser) -> Result<SizeKeyword, ()>
|
|
||||||
{
|
|
||||||
if let Ok(keyword) = input.try(parse_size_keyword) {
|
|
||||||
Ok(LengthOrKeyword::Keyword(keyword))
|
|
||||||
} else {
|
|
||||||
Ok(LengthOrKeyword::Length(try!(Length::parse(context, input))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for LengthOrKeyword {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
match *self {
|
|
||||||
LengthOrKeyword::Length(ref length) => length.to_css(dest),
|
|
||||||
LengthOrKeyword::Keyword(keyword) => keyword.to_css(dest),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum LengthOrPercentageOrKeyword {
|
|
||||||
LengthOrPercentage(LengthOrPercentage, LengthOrPercentage),
|
|
||||||
Keyword(SizeKeyword),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl LengthOrPercentageOrKeyword {
|
|
||||||
fn parse<F>(context: &ParserContext, input: &mut Parser, parse_size_keyword: F) -> Result<Self, ()>
|
|
||||||
where F: Fn(&mut Parser) -> Result<SizeKeyword, ()>
|
|
||||||
{
|
|
||||||
if let Ok(keyword) = input.try(parse_size_keyword) {
|
|
||||||
Ok(LengthOrPercentageOrKeyword::Keyword(keyword))
|
|
||||||
} else {
|
|
||||||
Ok(LengthOrPercentageOrKeyword::LengthOrPercentage(
|
|
||||||
try!(LengthOrPercentage::parse(context, input)),
|
|
||||||
try!(LengthOrPercentage::parse(context, input))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for LengthOrPercentageOrKeyword {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
match *self {
|
|
||||||
LengthOrPercentageOrKeyword::LengthOrPercentage(ref first_len, ref second_len) => {
|
|
||||||
try!(first_len.to_css(dest));
|
|
||||||
try!(dest.write_str(" "));
|
|
||||||
second_len.to_css(dest)
|
|
||||||
},
|
|
||||||
LengthOrPercentageOrKeyword::Keyword(keyword) => keyword.to_css(dest),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-images/#typedef-extent-keyword
|
|
||||||
define_css_keyword_enum!(SizeKeyword: "closest-side" => ClosestSide, "farthest-side" => FarthestSide,
|
|
||||||
"closest-corner" => ClosestCorner, "farthest-corner" => FarthestCorner,
|
|
||||||
"contain" => Contain, "cover" => Cover);
|
|
||||||
|
|
||||||
impl SizeKeyword {
|
|
||||||
fn parse_modern(input: &mut Parser) -> Result<Self, ()> {
|
|
||||||
match try!(SizeKeyword::parse(input)) {
|
|
||||||
SizeKeyword::Contain | SizeKeyword::Cover => Err(()),
|
|
||||||
keyword => Ok(keyword),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Specified values for none | <image> | <mask-source>.
|
/// Specified values for none | <image> | <mask-source>.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
|
|
@ -24,8 +24,8 @@ use values::computed::{ComputedValueAsSpecified, Context};
|
||||||
use values::specified::calc::CalcNode;
|
use values::specified::calc::CalcNode;
|
||||||
|
|
||||||
pub use values::specified::calc::CalcLengthOrPercentage;
|
pub use values::specified::calc::CalcLengthOrPercentage;
|
||||||
pub use super::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient};
|
pub use super::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
|
||||||
pub use super::image::{GradientKind, Image, LengthOrKeyword, LengthOrPercentageOrKeyword, SizeKeyword};
|
pub use super::image::{GradientKind, Image};
|
||||||
|
|
||||||
/// Number of app units per pixel
|
/// Number of app units per pixel
|
||||||
pub const AU_PER_PX: CSSFloat = 60.;
|
pub const AU_PER_PX: CSSFloat = 60.;
|
||||||
|
|
|
@ -28,9 +28,8 @@ use values::specified::calc::CalcNode;
|
||||||
pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
|
pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
|
||||||
pub use self::color::Color;
|
pub use self::color::Color;
|
||||||
pub use self::grid::{GridLine, TrackKeyword};
|
pub use self::grid::{GridLine, TrackKeyword};
|
||||||
pub use self::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient};
|
pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
|
||||||
pub use self::image::{GradientItem, GradientKind, Image, ImageRect, LayerImage};
|
pub use self::image::{GradientItem, GradientKind, Image, ImageRect, LayerImage};
|
||||||
pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword, SizeKeyword};
|
|
||||||
pub use self::length::AbsoluteLength;
|
pub use self::length::AbsoluteLength;
|
||||||
pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage};
|
pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage};
|
||||||
pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
|
pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||||
|
|
|
@ -2,16 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use euclid::size::TypedSize2D;
|
|
||||||
use parsing::parse;
|
use parsing::parse;
|
||||||
use std::f32::consts::PI;
|
|
||||||
use style::context::QuirksMode;
|
|
||||||
use style::font_metrics::ServoMetricsProvider;
|
|
||||||
use style::media_queries::{Device, MediaType};
|
|
||||||
use style::properties::{ComputedValues, StyleBuilder};
|
|
||||||
use style::values::computed;
|
|
||||||
use style::values::computed::{Angle, Context, ToComputedValue};
|
|
||||||
use style::values::specified;
|
|
||||||
use style::values::specified::image::*;
|
use style::values::specified::image::*;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
|
||||||
|
@ -37,101 +28,77 @@ fn test_linear_gradient() {
|
||||||
|
|
||||||
// Parsing without <angle> and <side-or-corner>
|
// Parsing without <angle> and <side-or-corner>
|
||||||
assert_roundtrip_with_context!(Image::parse, "linear-gradient(red, green)");
|
assert_roundtrip_with_context!(Image::parse, "linear-gradient(red, green)");
|
||||||
|
|
||||||
// AngleOrCorner::None should become AngleOrCorner::Angle(Angle(PI)) when parsed
|
|
||||||
// Note that Angle(PI) is correct for top-to-bottom rendering, whereas Angle(0) would render bottom-to-top.
|
|
||||||
// ref: https://developer.mozilla.org/en-US/docs/Web/CSS/angle
|
|
||||||
let viewport_size = TypedSize2D::new(0., 0.);
|
|
||||||
let initial_style = ComputedValues::initial_values();
|
|
||||||
let device = Device::new(MediaType::Screen, viewport_size);
|
|
||||||
let specified_context = Context {
|
|
||||||
is_root_element: true,
|
|
||||||
device: &device,
|
|
||||||
inherited_style: initial_style,
|
|
||||||
layout_parent_style: initial_style,
|
|
||||||
style: StyleBuilder::for_derived_style(&initial_style),
|
|
||||||
cached_system_font: None,
|
|
||||||
font_metrics_provider: &ServoMetricsProvider,
|
|
||||||
in_media_query: false,
|
|
||||||
quirks_mode: QuirksMode::NoQuirks,
|
|
||||||
};
|
|
||||||
assert_eq!(specified::AngleOrCorner::None.to_computed_value(&specified_context),
|
|
||||||
computed::AngleOrCorner::Angle(Angle::from_radians(PI)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_radial_gradient() {
|
fn test_radial_gradient() {
|
||||||
// Parsing with all values
|
// Parsing with all values
|
||||||
assert_roundtrip_with_context!(Image::parse, "radial-gradient(circle closest-side at 20px 30px, red, green)");
|
assert_roundtrip_with_context!(Image::parse, "radial-gradient(circle closest-side at 20px 30px, red, green)");
|
||||||
assert_roundtrip_with_context!(Image::parse, "radial-gradient(ellipse closest-side at 20px 30px, red, green)");
|
assert_roundtrip_with_context!(Image::parse, "radial-gradient(ellipse closest-side at 20px 30px, red, green)",
|
||||||
|
"radial-gradient(closest-side at 20px 30px, red, green)");
|
||||||
assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side circle at 20px 30px, red, green)",
|
assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side circle at 20px 30px, red, green)",
|
||||||
"radial-gradient(circle closest-side at 20px 30px, red, green)");
|
"radial-gradient(circle closest-side at 20px 30px, red, green)");
|
||||||
assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side ellipse at 20px 30px, red, green)",
|
assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side ellipse at 20px 30px, red, green)",
|
||||||
"radial-gradient(ellipse closest-side at 20px 30px, red, green)");
|
"radial-gradient(closest-side at 20px 30px, red, green)");
|
||||||
|
|
||||||
// Parsing with <shape-keyword> and <size> reversed
|
// Parsing with <shape-keyword> and <size> reversed
|
||||||
assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side circle at 20px 30px, red, green)",
|
assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side circle at 20px 30px, red, green)",
|
||||||
"radial-gradient(circle closest-side at 20px 30px, red, green)");
|
"radial-gradient(circle closest-side at 20px 30px, red, green)");
|
||||||
assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-corner ellipse at 20px 30px, red, green)",
|
assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-corner ellipse at 20px 30px, red, green)",
|
||||||
"radial-gradient(ellipse closest-corner at 20px 30px, red, green)");
|
"radial-gradient(closest-corner at 20px 30px, red, green)");
|
||||||
assert_roundtrip_with_context!(Image::parse, "radial-gradient(30px circle, red, green)",
|
assert_roundtrip_with_context!(Image::parse, "radial-gradient(30px circle, red, green)",
|
||||||
"radial-gradient(circle 30px at center center, red, green)");
|
"radial-gradient(30px at center center, red, green)");
|
||||||
assert_roundtrip_with_context!(Image::parse, "radial-gradient(30px 40px ellipse, red, green)",
|
assert_roundtrip_with_context!(Image::parse, "radial-gradient(30px 40px ellipse, red, green)",
|
||||||
"radial-gradient(ellipse 30px 40px at center center, red, green)");
|
"radial-gradient(30px 40px at center center, red, green)");
|
||||||
|
|
||||||
// Parsing without <size>
|
// Parsing without <size>
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(circle, red, green)",
|
"radial-gradient(circle, red, green)",
|
||||||
"radial-gradient(circle farthest-corner at center center, red, green)");
|
"radial-gradient(circle at center center, red, green)");
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(ellipse, red, green)",
|
"radial-gradient(ellipse, red, green)",
|
||||||
"radial-gradient(ellipse farthest-corner at center center, red, green)");
|
"radial-gradient(at center center, red, green)");
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(circle at 20px 30px, red, green)",
|
"radial-gradient(circle at 20px 30px, red, green)");
|
||||||
"radial-gradient(circle farthest-corner at 20px 30px, red, green)");
|
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(ellipse at 20px 30px, red, green)",
|
"radial-gradient(ellipse at 20px 30px, red, green)",
|
||||||
"radial-gradient(ellipse farthest-corner at 20px 30px, red, green)");
|
"radial-gradient(at 20px 30px, red, green)");
|
||||||
|
|
||||||
|
|
||||||
// Parsing without <shape-keyword>
|
// Parsing without <shape-keyword>
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(20px at 20px 30px, red, green)",
|
"radial-gradient(20px at 20px 30px, red, green)");
|
||||||
"radial-gradient(circle 20px at 20px 30px, red, green)");
|
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(20px 30px at left center, red, green)",
|
"radial-gradient(20px 30px at left center, red, green)");
|
||||||
"radial-gradient(ellipse 20px 30px at left center, red, green)");
|
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(closest-side at center, red, green)",
|
"radial-gradient(closest-side at center, red, green)",
|
||||||
"radial-gradient(ellipse closest-side at center center, red, green)");
|
"radial-gradient(closest-side at center center, red, green)");
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(20px, red, green)",
|
"radial-gradient(20px, red, green)",
|
||||||
"radial-gradient(circle 20px at center center, red, green)");
|
"radial-gradient(20px at center center, red, green)");
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(20px 30px, red, green)",
|
"radial-gradient(20px 30px, red, green)",
|
||||||
"radial-gradient(ellipse 20px 30px at center center, red, green)");
|
"radial-gradient(20px 30px at center center, red, green)");
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(closest-side, red, green)",
|
"radial-gradient(closest-side, red, green)",
|
||||||
"radial-gradient(ellipse closest-side at center center, red, green)");
|
"radial-gradient(closest-side at center center, red, green)");
|
||||||
|
|
||||||
// Parsing without <shape-keyword> and <size>
|
// Parsing without <shape-keyword> and <size>
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(at center, red, green)",
|
"radial-gradient(at center, red, green)",
|
||||||
"radial-gradient(ellipse farthest-corner at center center, red, green)");
|
"radial-gradient(at center center, red, green)");
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(at center bottom, red, green)",
|
"radial-gradient(at center bottom, red, green)");
|
||||||
"radial-gradient(ellipse farthest-corner at center bottom, red, green)");
|
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(at 40px 50px, red, green)",
|
"radial-gradient(at 40px 50px, red, green)");
|
||||||
"radial-gradient(ellipse farthest-corner at 40px 50px, red, green)");
|
|
||||||
|
|
||||||
// Parsing with just color stops
|
// Parsing with just color stops
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"radial-gradient(red, green)",
|
"radial-gradient(red, green)",
|
||||||
"radial-gradient(ellipse farthest-corner at center center, red, green)");
|
"radial-gradient(at center center, red, green)");
|
||||||
|
|
||||||
// Parsing repeating radial gradient
|
// Parsing repeating radial gradient
|
||||||
assert_roundtrip_with_context!(Image::parse,
|
assert_roundtrip_with_context!(Image::parse,
|
||||||
"repeating-radial-gradient(red, green)",
|
"repeating-radial-gradient(red, green)",
|
||||||
"repeating-radial-gradient(ellipse farthest-corner at center center, red, green)");
|
"repeating-radial-gradient(at center center, red, green)");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue