stylo: Implement -moz-prefixed radial gradients

This commit is contained in:
Nazım Can Altınova 2017-06-22 11:25:57 -07:00
parent 3e42684d3c
commit d589645ee9
5 changed files with 119 additions and 56 deletions

View file

@ -1339,7 +1339,7 @@ impl FragmentDisplayListBuilding for Fragment {
gradient: gradient, gradient: gradient,
}) })
} }
GradientKind::Radial(ref shape, ref center) => { GradientKind::Radial(ref shape, ref center, _angle) => {
let gradient = self.convert_radial_gradient(&bounds, let gradient = self.convert_radial_gradient(&bounds,
&gradient.items[..], &gradient.items[..],
shape, shape,
@ -1484,7 +1484,7 @@ impl FragmentDisplayListBuilding for Fragment {
}), }),
})); }));
} }
GradientKind::Radial(ref shape, ref center) => { GradientKind::Radial(ref shape, ref center, _angle) => {
let grad = self.convert_radial_gradient(&bounds, let grad = self.convert_radial_gradient(&bounds,
&gradient.items[..], &gradient.items[..],
shape, shape,

View file

@ -256,7 +256,7 @@ impl nsStyleImage {
} }
gecko_gradient gecko_gradient
}, },
GradientKind::Radial(shape, position) => { GradientKind::Radial(shape, position, angle) => {
let keyword_to_gecko_size = |keyword| { let keyword_to_gecko_size = |keyword| {
match keyword { match keyword {
ShapeExtent::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, ShapeExtent::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE,
@ -297,9 +297,14 @@ impl nsStyleImage {
stop_count as u32) stop_count as u32)
}; };
// Clear mAngle and mBgPos fields // Clear mBgPos field and set mAngle if angle is set. Otherwise clear it.
unsafe { unsafe {
(*gecko_gradient).mAngle.set_value(CoordDataValue::None); if let Some(angle) = angle {
(*gecko_gradient).mAngle.set(angle);
} else {
(*gecko_gradient).mAngle.set_value(CoordDataValue::None);
}
(*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);
} }

View file

@ -18,7 +18,6 @@ use values::generics::image::{CompatMode, ColorStop as GenericColorStop, EndingS
use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem}; use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem};
use values::generics::image::{Image as GenericImage, GradientKind as GenericGradientKind}; use values::generics::image::{Image as GenericImage, GradientKind as GenericGradientKind};
use values::generics::image::{ImageRect as GenericImageRect, LineDirection as GenericLineDirection}; use values::generics::image::{ImageRect as GenericImageRect, LineDirection as GenericLineDirection};
use values::generics::position::Position as GenericPosition;
use values::specified::image::{Gradient as SpecifiedGradient, LineDirection as SpecifiedLineDirection}; use values::specified::image::{Gradient as SpecifiedGradient, LineDirection as SpecifiedLineDirection};
use values::specified::image::{GradientKind as SpecifiedGradientKind}; use values::specified::image::{GradientKind as SpecifiedGradientKind};
use values::specified::position::{X, Y}; use values::specified::position::{X, Y};
@ -38,6 +37,7 @@ pub type Gradient = GenericGradient<
LengthOrPercentage, LengthOrPercentage,
Position, Position,
RGBA, RGBA,
Angle,
>; >;
/// A computed gradient kind. /// A computed gradient kind.
@ -46,6 +46,7 @@ pub type GradientKind = GenericGradientKind<
Length, Length,
LengthOrPercentage, LengthOrPercentage,
Position, Position,
Angle,
>; >;
/// A computed gradient line direction. /// A computed gradient line direction.
@ -205,9 +206,10 @@ impl SpecifiedGradientKind {
&GenericGradientKind::Linear(ref line_direction) => { &GenericGradientKind::Linear(ref line_direction) => {
GenericGradientKind::Linear(line_direction.to_computed_value(context, compat_mode)) GenericGradientKind::Linear(line_direction.to_computed_value(context, compat_mode))
}, },
&GenericGradientKind::Radial(ref ending_shape, ref position) => { &GenericGradientKind::Radial(ref ending_shape, ref position, ref angle) => {
GenericGradientKind::Radial(ending_shape.to_computed_value(context), GenericGradientKind::Radial(ending_shape.to_computed_value(context),
position.to_computed_value(context)) position.to_computed_value(context),
angle.map(|angle| angle.to_computed_value(context)))
} }
} }
} }
@ -218,9 +220,10 @@ impl SpecifiedGradientKind {
GenericGradientKind::Linear(line_direction) => { GenericGradientKind::Linear(line_direction) => {
GenericGradientKind::Linear(SpecifiedLineDirection::from_computed_value(&line_direction)) GenericGradientKind::Linear(SpecifiedLineDirection::from_computed_value(&line_direction))
}, },
GenericGradientKind::Radial(ending_shape, position) => { GenericGradientKind::Radial(ending_shape, position, angle) => {
GenericGradientKind::Radial(GenericEndingShape::from_computed_value(&ending_shape), GenericGradientKind::Radial(ToComputedValue::from_computed_value(&ending_shape),
GenericPosition::from_computed_value(&position)) ToComputedValue::from_computed_value(&position),
angle.map(|angle| ToComputedValue::from_computed_value(&angle)))
} }
} }
} }

View file

@ -37,9 +37,9 @@ pub enum Image<Gradient, ImageRect> {
/// https://drafts.csswg.org/css-images/#gradients /// https://drafts.csswg.org/css-images/#gradients
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)] #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color> { pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color, Angle> {
/// Gradients can be linear or radial. /// Gradients can be linear or radial.
pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position>, pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle>,
/// The color stops and interpolation hints. /// The color stops and interpolation hints.
pub items: Vec<GradientItem<Color, LengthOrPercentage>>, pub items: Vec<GradientItem<Color, LengthOrPercentage>>,
/// True if this is a repeating gradient. /// True if this is a repeating gradient.
@ -63,11 +63,11 @@ pub enum CompatMode {
/// A gradient kind. /// A gradient kind.
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)] #[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position> { pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle> {
/// A linear gradient. /// A linear gradient.
Linear(LineDirection), Linear(LineDirection),
/// A radial gradient. /// A radial gradient.
Radial(EndingShape<Length, LengthOrPercentage>, Position), Radial(EndingShape<Length, LengthOrPercentage>, Position, Option<Angle>),
} }
/// A radial gradient's ending shape. /// A radial gradient's ending shape.
@ -214,8 +214,8 @@ impl<G, R> HasViewportPercentage for Image<G, R>
} }
} }
impl<D, L, LoP, P, C> ToCss for Gradient<D, L, LoP, P, C> impl<D, L, LoP, P, C, A> ToCss for Gradient<D, L, LoP, P, C, A>
where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, A: 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.compat_mode { match self.compat_mode {
@ -235,7 +235,7 @@ impl<D, L, LoP, P, C> ToCss for Gradient<D, L, LoP, P, C>
direction.to_css(dest, self.compat_mode)?; direction.to_css(dest, self.compat_mode)?;
false false
}, },
GradientKind::Radial(ref shape, ref position) => { GradientKind::Radial(ref shape, ref position, ref angle) => {
let omit_shape = match *shape { let omit_shape = match *shape {
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) | EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => { EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => {
@ -252,6 +252,10 @@ impl<D, L, LoP, P, C> ToCss for Gradient<D, L, LoP, P, C>
position.to_css(dest)?; position.to_css(dest)?;
} else { } else {
position.to_css(dest)?; position.to_css(dest)?;
if let Some(ref a) = *angle {
dest.write_str(" ")?;
a.to_css(dest)?;
}
if !omit_shape { if !omit_shape {
dest.write_str(", ")?; dest.write_str(", ")?;
shape.to_css(dest)?; shape.to_css(dest)?;
@ -271,7 +275,7 @@ impl<D, L, LoP, P, C> ToCss for Gradient<D, L, LoP, P, C>
} }
} }
impl<D, L, LoP, P> GradientKind<D, L, LoP, P> { impl<D, L, LoP, P, A> GradientKind<D, L, LoP, P, A> {
fn label(&self) -> &str { fn label(&self) -> &str {
match *self { match *self {
GradientKind::Linear(..) => "linear", GradientKind::Linear(..) => "linear",

View file

@ -46,6 +46,7 @@ pub type Gradient = GenericGradient<
LengthOrPercentage, LengthOrPercentage,
Position, Position,
RGBAColor, RGBAColor,
Angle,
>; >;
/// A specified gradient kind. /// A specified gradient kind.
@ -54,6 +55,7 @@ pub type GradientKind = GenericGradientKind<
Length, Length,
LengthOrPercentage, LengthOrPercentage,
Position, Position,
Angle,
>; >;
/// A specified gradient line direction. /// A specified gradient line direction.
@ -185,6 +187,10 @@ impl Parse for Gradient {
}, },
"-webkit-radial-gradient" => { "-webkit-radial-gradient" => {
Some((Shape::Radial, false, CompatMode::WebKit)) Some((Shape::Radial, false, CompatMode::WebKit))
}
#[cfg(feature = "gecko")]
"-moz-radial-gradient" => {
Some((Shape::Radial, false, CompatMode::Moz))
}, },
"repeating-radial-gradient" => { "repeating-radial-gradient" => {
Some((Shape::Radial, true, CompatMode::Modern)) Some((Shape::Radial, true, CompatMode::Modern))
@ -192,6 +198,10 @@ impl Parse for Gradient {
"-webkit-repeating-radial-gradient" => { "-webkit-repeating-radial-gradient" => {
Some((Shape::Radial, true, CompatMode::WebKit)) Some((Shape::Radial, true, CompatMode::WebKit))
}, },
#[cfg(feature = "gecko")]
"-moz-repeating-radial-gradient" => {
Some((Shape::Radial, true, CompatMode::Moz))
},
"-webkit-gradient" => { "-webkit-gradient" => {
return input.parse_nested_block(|i| Self::parse_webkit_gradient_argument(context, i)); return input.parse_nested_block(|i| Self::parse_webkit_gradient_argument(context, i));
}, },
@ -206,7 +216,7 @@ impl Parse for Gradient {
let (kind, items) = input.parse_nested_block(|i| { let (kind, items) = input.parse_nested_block(|i| {
let shape = match shape { let shape = match shape {
Shape::Linear => GradientKind::parse_linear(context, i, &mut compat_mode)?, Shape::Linear => GradientKind::parse_linear(context, i, &mut compat_mode)?,
Shape::Radial => GradientKind::parse_radial(context, i, compat_mode)?, Shape::Radial => GradientKind::parse_radial(context, i, &mut compat_mode)?,
}; };
let items = GradientItem::parse_comma_separated(context, i)?; let items = GradientItem::parse_comma_separated(context, i)?;
Ok((shape, items)) Ok((shape, items))
@ -386,7 +396,7 @@ impl Gradient {
let shape = GenericEndingShape::Circle(Circle::Radius(Length::from_px(radius.value))); let shape = GenericEndingShape::Circle(Circle::Radius(Length::from_px(radius.value)));
let position = point.into(); let position = point.into();
let kind = GenericGradientKind::Radial(shape, position); let kind = GenericGradientKind::Radial(shape, position, None);
(kind, reverse_stops) (kind, reverse_stops)
}, },
@ -492,27 +502,56 @@ impl GradientKind {
fn parse_radial<'i, 't>(context: &ParserContext, fn parse_radial<'i, 't>(context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
compat_mode: CompatMode) compat_mode: &mut CompatMode)
-> Result<Self, ParseError<'i>> { -> Result<Self, ParseError<'i>> {
let (shape, position) = if compat_mode == CompatMode::Modern { let (shape, position, angle) = match *compat_mode {
let shape = input.try(|i| EndingShape::parse(context, i, compat_mode)); CompatMode::Modern => {
let position = input.try(|i| { let shape = input.try(|i| EndingShape::parse(context, i, *compat_mode));
i.expect_ident_matching("at")?; let position = input.try(|i| {
Position::parse(context, i) i.expect_ident_matching("at")?;
}); Position::parse(context, i)
(shape, position) });
} else { (shape, position, None)
let position = input.try(|i| Position::parse(context, i)); },
let shape = input.try(|i| { CompatMode::WebKit => {
if position.is_ok() { let position = input.try(|i| Position::parse(context, i));
i.expect_comma()?; let shape = input.try(|i| {
if position.is_ok() {
i.expect_comma()?;
}
EndingShape::parse(context, i, *compat_mode)
});
(shape, position, None)
},
// The syntax of `-moz-` prefixed radial gradient is:
// -moz-radial-gradient(
// [ [ <position> || <angle> ]? [ ellipse | [ <length> | <percentage> ]{2} ] , |
// [ <position> || <angle> ]? [ [ circle | ellipse ] | <extent-keyword> ] , |
// ]?
// <color-stop> [ , <color-stop> ]+
// )
// where <extent-keyword> = closest-corner | closest-side | farthest-corner | farthest-side |
// cover | contain
// and <color-stop> = <color> [ <percentage> | <length> ]?
CompatMode::Moz => {
let mut position = input.try(|i| Position::parse_legacy(context, i));
let angle = input.try(|i| Angle::parse(context, i)).ok();
if position.is_err() {
position = input.try(|i| Position::parse_legacy(context, i));
} }
EndingShape::parse(context, i, compat_mode)
}); let shape = input.try(|i| {
(shape, position) if position.is_ok() || angle.is_some() {
i.expect_comma()?;
}
EndingShape::parse(context, i, *compat_mode)
});
(shape, position, angle)
}
}; };
if shape.is_ok() || position.is_ok() { if shape.is_ok() || position.is_ok() || angle.is_some() {
input.expect_comma()?; input.expect_comma()?;
} }
@ -520,7 +559,11 @@ impl GradientKind {
GenericEndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) GenericEndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner))
}); });
let position = position.unwrap_or(Position::center()); let position = position.unwrap_or(Position::center());
Ok(GenericGradientKind::Radial(shape, position)) // If this form can be represented in Modern mode, then convert the compat_mode to Modern.
if *compat_mode == CompatMode::Moz && angle.is_none() {
*compat_mode = CompatMode::Modern;
}
Ok(GenericGradientKind::Radial(shape, position, angle))
} }
} }
@ -681,24 +724,29 @@ impl EndingShape {
} }
return Ok(GenericEndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner))); return Ok(GenericEndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)));
} }
if let Ok(length) = input.try(|i| Length::parse(context, i)) { // -moz- prefixed radial gradient doesn't allow EndingShape's Length or LengthOrPercentage
if let Ok(y) = input.try(|i| LengthOrPercentage::parse(context, i)) { // to come before shape keyword. Otherwise it conflicts with <position>.
if compat_mode == CompatMode::Modern { if compat_mode != CompatMode::Moz {
let _ = input.try(|i| i.expect_ident_matching("ellipse")); if let Ok(length) = input.try(|i| Length::parse(context, i)) {
} if let Ok(y) = input.try(|i| LengthOrPercentage::parse(context, i)) {
return Ok(GenericEndingShape::Ellipse(Ellipse::Radii(length.into(), y))); if compat_mode == CompatMode::Modern {
} let _ = input.try(|i| i.expect_ident_matching("ellipse"));
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))); return Ok(GenericEndingShape::Ellipse(Ellipse::Radii(length.into(), y)));
} }
let _ = input.try(|i| i.expect_ident_matching("circle")); 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)));
} }
return Ok(GenericEndingShape::Circle(Circle::Radius(length)));
} }
input.try(|i| { input.try(|i| {
let x = Percentage::parse(context, i)?; let x = Percentage::parse(context, i)?;
@ -723,8 +771,11 @@ impl ShapeExtent {
compat_mode: CompatMode) compat_mode: CompatMode)
-> Result<Self, ParseError<'i>> { -> Result<Self, ParseError<'i>> {
match Self::parse(input)? { match Self::parse(input)? {
ShapeExtent::Contain | ShapeExtent::Cover if compat_mode == CompatMode::Modern => ShapeExtent::Contain | ShapeExtent::Cover if compat_mode == CompatMode::Modern => {
Err(StyleParseError::UnspecifiedError.into()), Err(StyleParseError::UnspecifiedError.into())
},
ShapeExtent::Contain => Ok(ShapeExtent::ClosestSide),
ShapeExtent::Cover => Ok(ShapeExtent::FarthestCorner),
keyword => Ok(keyword), keyword => Ok(keyword),
} }
} }