aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/layout/display_list_builder.rs4
-rw-r--r--components/style/gecko/conversions.rs36
-rw-r--r--components/style/gecko/generated/bindings.rs4
-rw-r--r--components/style/values/computed/image.rs49
-rw-r--r--components/style/values/generics/image.rs31
-rw-r--r--components/style/values/specified/image.rs294
-rw-r--r--components/style/values/specified/position.rs111
-rw-r--r--components/style/values/specified/transform.rs7
8 files changed, 455 insertions, 81 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index 7ab6c7f78b7..9aa9301eef8 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -1339,7 +1339,7 @@ impl FragmentDisplayListBuilding for Fragment {
gradient: gradient,
})
}
- GradientKind::Radial(ref shape, ref center) => {
+ GradientKind::Radial(ref shape, ref center, _angle) => {
let gradient = self.convert_radial_gradient(&bounds,
&gradient.items[..],
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,
&gradient.items[..],
shape,
diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs
index ac87b180953..9b56f732835 100644
--- a/components/style/gecko/conversions.rs
+++ b/components/style/gecko/conversions.rs
@@ -205,7 +205,8 @@ impl nsStyleImage {
Gecko_CreateGradient(NS_STYLE_GRADIENT_SHAPE_LINEAR as u8,
NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as u8,
gradient.repeating,
- gradient.compat_mode == CompatMode::WebKit,
+ gradient.compat_mode != CompatMode::Modern,
+ gradient.compat_mode == CompatMode::Moz,
stop_count as u32)
};
@@ -234,11 +235,28 @@ impl nsStyleImage {
(*gecko_gradient).mBgPosY
.set_value(CoordDataValue::Percent(percent_y));
}
- }
+ },
+ #[cfg(feature = "gecko")]
+ LineDirection::MozPosition(position, angle) => {
+ unsafe {
+ if let Some(position) = position {
+ (*gecko_gradient).mBgPosX.set(position.horizontal);
+ (*gecko_gradient).mBgPosY.set(position.vertical);
+ } else {
+ (*gecko_gradient).mBgPosX.set_value(CoordDataValue::None);
+ (*gecko_gradient).mBgPosY.set_value(CoordDataValue::None);
+ }
+ if let Some(angle) = angle {
+ (*gecko_gradient).mAngle.set(angle);
+ } else {
+ (*gecko_gradient).mAngle.set_value(CoordDataValue::None);
+ }
+ }
+ },
}
gecko_gradient
},
- GradientKind::Radial(shape, position) => {
+ GradientKind::Radial(shape, position, angle) => {
let keyword_to_gecko_size = |keyword| {
match keyword {
ShapeExtent::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE,
@@ -274,13 +292,19 @@ impl nsStyleImage {
Gecko_CreateGradient(gecko_shape,
gecko_size,
gradient.repeating,
- gradient.compat_mode == CompatMode::WebKit,
+ gradient.compat_mode != CompatMode::Modern,
+ gradient.compat_mode == CompatMode::Moz,
stop_count as u32)
};
- // Clear mAngle and mBgPos fields
+ // Clear mBgPos field and set mAngle if angle is set. Otherwise clear it.
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).mBgPosY.set_value(CoordDataValue::None);
}
diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs
index 006f2a8ddd3..6db35db4954 100644
--- a/components/style/gecko/generated/bindings.rs
+++ b/components/style/gecko/generated/bindings.rs
@@ -952,8 +952,8 @@ extern "C" {
}
extern "C" {
pub fn Gecko_CreateGradient(shape: u8, size: u8, repeating: bool,
- legacy_syntax: bool, stops: u32)
- -> *mut nsStyleGradient;
+ legacy_syntax: bool, moz_legacy_syntax: bool,
+ stops: u32) -> *mut nsStyleGradient;
}
extern "C" {
pub fn Gecko_SetListStyleImageNone(style_struct: *mut nsStyleList);
diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs
index 05640c94f6f..1e2b694d51d 100644
--- a/components/style/values/computed/image.rs
+++ b/components/style/values/computed/image.rs
@@ -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::{Image as GenericImage, GradientKind as GenericGradientKind};
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::{GradientKind as SpecifiedGradientKind};
use values::specified::position::{X, Y};
@@ -38,6 +37,7 @@ pub type Gradient = GenericGradient<
LengthOrPercentage,
Position,
RGBA,
+ Angle,
>;
/// A computed gradient kind.
@@ -46,6 +46,7 @@ pub type GradientKind = GenericGradientKind<
Length,
LengthOrPercentage,
Position,
+ Angle,
>;
/// A computed gradient line direction.
@@ -56,6 +57,9 @@ pub enum LineDirection {
Angle(Angle),
/// A corner.
Corner(X, Y),
+ /// A Position and an Angle for legacy `-moz-` prefixed gradient.
+ #[cfg(feature = "gecko")]
+ MozPosition(Option<Position>, Option<Angle>),
}
/// A computed radial gradient ending shape.
@@ -75,6 +79,8 @@ impl GenericLineDirection for LineDirection {
match *self {
LineDirection::Angle(angle) => angle.radians() == PI,
LineDirection::Corner(..) => false,
+ #[cfg(feature = "gecko")]
+ LineDirection::MozPosition(_, _) => false,
}
}
@@ -91,6 +97,21 @@ impl GenericLineDirection for LineDirection {
dest.write_str(" ")?;
y.to_css(dest)
},
+ #[cfg(feature = "gecko")]
+ LineDirection::MozPosition(position, angle) => {
+ let mut need_space = false;
+ if let Some(position) = position {
+ position.to_css(dest)?;
+ need_space = true;
+ }
+ if let Some(angle) = angle {
+ if need_space {
+ dest.write_str(" ")?;
+ }
+ angle.to_css(dest)?;
+ }
+ Ok(())
+ }
}
}
}
@@ -102,8 +123,8 @@ impl SpecifiedLineDirection {
#[cfg(feature = "gecko")]
{
return match _compat_mode {
- CompatMode::Modern => modern_angle,
- CompatMode::WebKit => -modern_angle + 270.
+ CompatMode::WebKit => -modern_angle + 270.,
+ _ => modern_angle,
}
}
#[cfg(feature = "servo")]
@@ -131,6 +152,11 @@ impl SpecifiedLineDirection {
SpecifiedLineDirection::Corner(x, y) => {
LineDirection::Corner(x, y)
},
+ #[cfg(feature = "gecko")]
+ SpecifiedLineDirection::MozPosition(ref position, ref angle) => {
+ LineDirection::MozPosition(position.to_computed_value(context),
+ angle.to_computed_value(context))
+ },
}
}
@@ -142,6 +168,11 @@ impl SpecifiedLineDirection {
LineDirection::Corner(x, y) => {
SpecifiedLineDirection::Corner(x, y)
},
+ #[cfg(feature = "gecko")]
+ LineDirection::MozPosition(ref position, ref angle) => {
+ SpecifiedLineDirection::MozPosition(ToComputedValue::from_computed_value(position),
+ ToComputedValue::from_computed_value(angle))
+ },
}
}
}
@@ -175,9 +206,10 @@ impl SpecifiedGradientKind {
&GenericGradientKind::Linear(ref line_direction) => {
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),
- position.to_computed_value(context))
+ position.to_computed_value(context),
+ angle.map(|angle| angle.to_computed_value(context)))
}
}
}
@@ -188,9 +220,10 @@ impl SpecifiedGradientKind {
GenericGradientKind::Linear(line_direction) => {
GenericGradientKind::Linear(SpecifiedLineDirection::from_computed_value(&line_direction))
},
- GenericGradientKind::Radial(ending_shape, position) => {
- GenericGradientKind::Radial(GenericEndingShape::from_computed_value(&ending_shape),
- GenericPosition::from_computed_value(&position))
+ GenericGradientKind::Radial(ending_shape, position, angle) => {
+ GenericGradientKind::Radial(ToComputedValue::from_computed_value(&ending_shape),
+ ToComputedValue::from_computed_value(&position),
+ angle.map(|angle| ToComputedValue::from_computed_value(&angle)))
}
}
}
diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs
index 31a4606aa79..78d011beac0 100644
--- a/components/style/values/generics/image.rs
+++ b/components/style/values/generics/image.rs
@@ -37,9 +37,9 @@ pub enum Image<Gradient, ImageRect> {
/// https://drafts.csswg.org/css-images/#gradients
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
#[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.
- pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position>,
+ pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle>,
/// The color stops and interpolation hints.
pub items: Vec<GradientItem<Color, LengthOrPercentage>>,
/// True if this is a repeating gradient.
@@ -50,22 +50,24 @@ pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color>
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-/// Whether we used the modern notation or the compatibility `-webkit` prefix.
+/// Whether we used the modern notation or the compatibility `-webkit`, `-moz` prefixes.
pub enum CompatMode {
/// Modern syntax.
Modern,
/// `-webkit` prefix.
WebKit,
+ /// `-moz` prefix
+ Moz,
}
/// A gradient kind.
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position> {
+pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle> {
/// A linear gradient.
Linear(LineDirection),
/// A radial gradient.
- Radial(EndingShape<Length, LengthOrPercentage>, Position),
+ Radial(EndingShape<Length, LengthOrPercentage>, Position, Option<Angle>),
}
/// A radial gradient's ending shape.
@@ -212,13 +214,16 @@ impl<G, R> HasViewportPercentage for Image<G, R>
}
}
-impl<D, L, LoP, P, C> ToCss for Gradient<D, L, LoP, P, C>
- where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss,
+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, A: 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-")?;
+ match self.compat_mode {
+ CompatMode::WebKit => dest.write_str("-webkit-")?,
+ CompatMode::Moz => dest.write_str("-moz-")?,
+ _ => {},
}
+
if self.repeating {
dest.write_str("repeating-")?;
}
@@ -230,7 +235,7 @@ impl<D, L, LoP, P, C> ToCss for Gradient<D, L, LoP, P, C>
direction.to_css(dest, self.compat_mode)?;
false
},
- GradientKind::Radial(ref shape, ref position) => {
+ GradientKind::Radial(ref shape, ref position, ref angle) => {
let omit_shape = match *shape {
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => {
@@ -247,6 +252,10 @@ impl<D, L, LoP, P, C> ToCss for Gradient<D, L, LoP, P, C>
position.to_css(dest)?;
} else {
position.to_css(dest)?;
+ if let Some(ref a) = *angle {
+ dest.write_str(" ")?;
+ a.to_css(dest)?;
+ }
if !omit_shape {
dest.write_str(", ")?;
shape.to_css(dest)?;
@@ -266,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 {
match *self {
GradientKind::Linear(..) => "linear",
diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs
index 7fc73f94733..5bd143a7a22 100644
--- a/components/style/values/specified/image.rs
+++ b/components/style/values/specified/image.rs
@@ -19,6 +19,8 @@ use std::f32::consts::PI;
use std::fmt;
use style_traits::{ToCss, ParseError, StyleParseError};
use values::{Either, None_};
+#[cfg(feature = "gecko")]
+use values::computed::{Context, Position as ComputedPosition, ToComputedValue};
use values::generics::image::{Circle, CompatMode, Ellipse, ColorStop as GenericColorStop};
use values::generics::image::{EndingShape as GenericEndingShape, Gradient as GenericGradient};
use values::generics::image::{GradientItem as GenericGradientItem, GradientKind as GenericGradientKind};
@@ -28,7 +30,7 @@ use values::generics::image::PaintWorklet;
use values::generics::position::Position as GenericPosition;
use values::specified::{Angle, Color, Length, LengthOrPercentage};
use values::specified::{Number, NumberOrPercentage, Percentage, RGBAColor};
-use values::specified::position::{Position, PositionComponent, Side, X, Y};
+use values::specified::position::{LegacyPosition, Position, PositionComponent, Side, X, Y};
use values::specified::url::SpecifiedUrl;
/// A specified image layer.
@@ -40,20 +42,46 @@ pub type Image = GenericImage<Gradient, ImageRect>;
/// Specified values for a CSS gradient.
/// https://drafts.csswg.org/css-images/#gradients
+#[cfg(not(feature = "gecko"))]
pub type Gradient = GenericGradient<
LineDirection,
Length,
LengthOrPercentage,
Position,
RGBAColor,
+ Angle,
+>;
+
+/// Specified values for a CSS gradient.
+/// https://drafts.csswg.org/css-images/#gradients
+#[cfg(feature = "gecko")]
+pub type Gradient = GenericGradient<
+ LineDirection,
+ Length,
+ LengthOrPercentage,
+ GradientPosition,
+ RGBAColor,
+ Angle,
>;
/// A specified gradient kind.
+#[cfg(not(feature = "gecko"))]
pub type GradientKind = GenericGradientKind<
LineDirection,
Length,
LengthOrPercentage,
Position,
+ Angle,
+>;
+
+/// A specified gradient kind.
+#[cfg(feature = "gecko")]
+pub type GradientKind = GenericGradientKind<
+ LineDirection,
+ Length,
+ LengthOrPercentage,
+ GradientPosition,
+ Angle,
>;
/// A specified gradient line direction.
@@ -68,6 +96,22 @@ pub enum LineDirection {
Vertical(Y),
/// A direction towards a corner of a box.
Corner(X, Y),
+ /// A Position and an Angle for legacy `-moz-` prefixed gradient.
+ /// `-moz-` prefixed linear gradient can contain both a position and an angle but it
+ /// uses legacy syntax for position. That means we can't specify both keyword and
+ /// length for each horizontal/vertical components.
+ #[cfg(feature = "gecko")]
+ MozPosition(Option<LegacyPosition>, Option<Angle>),
+}
+
+/// A binary enum to hold either Position or LegacyPosition.
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
+#[cfg(feature = "gecko")]
+pub enum GradientPosition {
+ /// 1, 2, 3, 4-valued <position>.
+ Modern(Position),
+ /// 1, 2-valued <position>.
+ Legacy(LegacyPosition),
}
/// A specified ending shape.
@@ -160,17 +204,29 @@ impl Parse for Gradient {
"-webkit-linear-gradient" => {
Some((Shape::Linear, false, CompatMode::WebKit))
},
+ #[cfg(feature = "gecko")]
+ "-moz-linear-gradient" => {
+ Some((Shape::Linear, false, CompatMode::Moz))
+ },
"repeating-linear-gradient" => {
Some((Shape::Linear, true, CompatMode::Modern))
},
"-webkit-repeating-linear-gradient" => {
Some((Shape::Linear, true, CompatMode::WebKit))
},
+ #[cfg(feature = "gecko")]
+ "-moz-repeating-linear-gradient" => {
+ Some((Shape::Linear, true, CompatMode::Moz))
+ },
"radial-gradient" => {
Some((Shape::Radial, false, CompatMode::Modern))
},
"-webkit-radial-gradient" => {
Some((Shape::Radial, false, CompatMode::WebKit))
+ }
+ #[cfg(feature = "gecko")]
+ "-moz-radial-gradient" => {
+ Some((Shape::Radial, false, CompatMode::Moz))
},
"repeating-radial-gradient" => {
Some((Shape::Radial, true, CompatMode::Modern))
@@ -178,21 +234,25 @@ impl Parse for Gradient {
"-webkit-repeating-radial-gradient" => {
Some((Shape::Radial, true, CompatMode::WebKit))
},
+ #[cfg(feature = "gecko")]
+ "-moz-repeating-radial-gradient" => {
+ Some((Shape::Radial, true, CompatMode::Moz))
+ },
"-webkit-gradient" => {
return input.parse_nested_block(|i| Self::parse_webkit_gradient_argument(context, i));
},
_ => None,
};
- let (shape, repeating, compat_mode) = match result {
+ let (shape, repeating, mut compat_mode) = match result {
Some(result) => result,
None => return Err(StyleParseError::UnexpectedFunction(func).into()),
};
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)?,
+ Shape::Linear => GradientKind::parse_linear(context, i, &mut compat_mode)?,
+ Shape::Radial => GradientKind::parse_radial(context, i, &mut compat_mode)?,
};
let items = GradientItem::parse_comma_separated(context, i)?;
Ok((shape, items))
@@ -371,10 +431,19 @@ impl Gradient {
};
let shape = GenericEndingShape::Circle(Circle::Radius(Length::from_px(radius.value)));
- let position = point.into();
- let kind = GenericGradientKind::Radial(shape, position);
+ let position: Position = point.into();
- (kind, reverse_stops)
+ #[cfg(feature = "gecko")]
+ {
+ let kind = GenericGradientKind::Radial(shape, GradientPosition::Modern(position), None);
+ (kind, reverse_stops)
+ }
+
+ #[cfg(not(feature = "gecko"))]
+ {
+ let kind = GenericGradientKind::Radial(shape, position, None);
+ (kind, reverse_stops)
+ }
},
_ => return Err(SelectorParseError::UnexpectedIdent(ident.clone()).into()),
};
@@ -461,9 +530,11 @@ impl Gradient {
}
impl GradientKind {
+ /// Parses a linear gradient.
+ /// CompatMode can change during `-moz-` prefixed gradient parsing if it come across a `to` keyword.
fn parse_linear<'i, 't>(context: &ParserContext,
input: &mut Parser<'i, 't>,
- compat_mode: CompatMode)
+ compat_mode: &mut CompatMode)
-> Result<Self, ParseError<'i>> {
let direction = if let Ok(d) = input.try(|i| LineDirection::parse(context, i, compat_mode)) {
input.expect_comma()?;
@@ -476,35 +547,84 @@ impl GradientKind {
fn parse_radial<'i, 't>(context: &ParserContext,
input: &mut Parser<'i, 't>,
- compat_mode: CompatMode)
+ compat_mode: &mut CompatMode)
-> Result<Self, ParseError<'i>> {
- 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 {
- let position = input.try(|i| Position::parse(context, i));
- let shape = input.try(|i| {
- if position.is_ok() {
- i.expect_comma()?;
+ let (shape, position, angle, moz_position) = match *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.ok(), None, None)
+ },
+ CompatMode::WebKit => {
+ let position = input.try(|i| Position::parse(context, i));
+ let shape = input.try(|i| {
+ if position.is_ok() {
+ i.expect_comma()?;
+ }
+ EndingShape::parse(context, i, *compat_mode)
+ });
+ (shape, position.ok(), None, 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| LegacyPosition::parse(context, i));
+ let angle = input.try(|i| Angle::parse(context, i)).ok();
+ if position.is_err() {
+ position = input.try(|i| LegacyPosition::parse(context, i));
}
- EndingShape::parse(context, i, compat_mode)
- });
- (shape, position)
+
+ let shape = input.try(|i| {
+ if position.is_ok() || angle.is_some() {
+ i.expect_comma()?;
+ }
+ EndingShape::parse(context, i, *compat_mode)
+ });
+
+ (shape, None, angle, position.ok())
+ }
};
- if shape.is_ok() || position.is_ok() {
+ if shape.is_ok() || position.is_some() || angle.is_some() || moz_position.is_some() {
input.expect_comma()?;
}
let shape = shape.unwrap_or({
GenericEndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner))
});
+
+ #[cfg(feature = "gecko")]
+ {
+ if *compat_mode == CompatMode::Moz {
+ // If this form can be represented in Modern mode, then convert the compat_mode to Modern.
+ if angle.is_none() {
+ *compat_mode = CompatMode::Modern;
+ }
+ let position = moz_position.unwrap_or(LegacyPosition::center());
+ return Ok(GenericGradientKind::Radial(shape, GradientPosition::Legacy(position), angle));
+ }
+ }
+
let position = position.unwrap_or(Position::center());
- Ok(GenericGradientKind::Radial(shape, position))
+ #[cfg(feature = "gecko")]
+ {
+ return Ok(GenericGradientKind::Radial(shape, GradientPosition::Modern(position), angle));
+ }
+ #[cfg(not(feature = "gecko"))]
+ {
+ return Ok(GenericGradientKind::Radial(shape, position, angle));
+ }
}
}
@@ -543,7 +663,22 @@ impl GenericsLineDirection for LineDirection {
x.to_css(dest)?;
dest.write_str(" ")?;
y.to_css(dest)
- }
+ },
+ #[cfg(feature = "gecko")]
+ LineDirection::MozPosition(ref position, ref angle) => {
+ let mut need_space = false;
+ if let Some(ref position) = *position {
+ position.to_css(dest)?;
+ need_space = true;
+ }
+ if let Some(ref angle) = *angle {
+ if need_space {
+ dest.write_str(" ")?;
+ }
+ angle.to_css(dest)?;
+ }
+ Ok(())
+ },
}
}
}
@@ -551,15 +686,50 @@ impl GenericsLineDirection for LineDirection {
impl LineDirection {
fn parse<'i, 't>(context: &ParserContext,
input: &mut Parser<'i, 't>,
- compat_mode: CompatMode)
+ compat_mode: &mut CompatMode)
-> Result<Self, ParseError<'i>> {
- if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) {
- return Ok(LineDirection::Angle(angle));
- }
+ let mut _angle = if *compat_mode == CompatMode::Moz {
+ input.try(|i| Angle::parse(context, i)).ok()
+ } else {
+ if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) {
+ return Ok(LineDirection::Angle(angle));
+ }
+ None
+ };
+
input.try(|i| {
- if compat_mode == CompatMode::Modern {
- i.expect_ident_matching("to")?;
+ let to_ident = i.try(|i| i.expect_ident_matching("to"));
+ match *compat_mode {
+ /// `to` keyword is mandatory in modern syntax.
+ CompatMode::Modern => to_ident?,
+ // Fall back to Modern compatibility mode in case there is a `to` keyword.
+ // According to Gecko, `-moz-linear-gradient(to ...)` should serialize like
+ // `linear-gradient(to ...)`.
+ CompatMode::Moz if to_ident.is_ok() => *compat_mode = CompatMode::Modern,
+ /// There is no `to` keyword in webkit prefixed syntax. If it's consumed,
+ /// parsing should throw an error.
+ CompatMode::WebKit if to_ident.is_ok() => {
+ return Err(SelectorParseError::UnexpectedIdent("to".into()).into())
+ },
+ _ => {},
+ }
+
+ #[cfg(feature = "gecko")]
+ {
+ // `-moz-` prefixed linear gradient can be both Angle and Position.
+ if *compat_mode == CompatMode::Moz {
+ let position = i.try(|i| LegacyPosition::parse(context, i)).ok();
+ if _angle.is_none() {
+ _angle = i.try(|i| Angle::parse(context, i)).ok();
+ };
+
+ if _angle.is_none() && position.is_none() {
+ return Err(StyleParseError::UnspecifiedError.into());
+ }
+ return Ok(LineDirection::MozPosition(position, _angle));
+ }
}
+
if let Ok(x) = i.try(X::parse) {
if let Ok(y) = i.try(Y::parse) {
return Ok(LineDirection::Corner(x, y));
@@ -575,6 +745,22 @@ impl LineDirection {
}
}
+#[cfg(feature = "gecko")]
+impl ToComputedValue for GradientPosition {
+ type ComputedValue = ComputedPosition;
+
+ fn to_computed_value(&self, context: &Context) -> ComputedPosition {
+ match *self {
+ GradientPosition::Modern(ref pos) => pos.to_computed_value(context),
+ GradientPosition::Legacy(ref pos) => pos.to_computed_value(context),
+ }
+ }
+
+ fn from_computed_value(computed: &ComputedPosition) -> Self {
+ GradientPosition::Modern(ToComputedValue::from_computed_value(computed))
+ }
+}
+
impl EndingShape {
fn parse<'i, 't>(context: &ParserContext,
input: &mut Parser<'i, 't>,
@@ -614,24 +800,29 @@ impl EndingShape {
}
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 {
+ // -moz- prefixed radial gradient doesn't allow EndingShape's Length or LengthOrPercentage
+ // to come before shape keyword. Otherwise it conflicts with <position>.
+ if compat_mode != CompatMode::Moz {
+ 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)));
}
- 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| {
let x = Percentage::parse(context, i)?;
@@ -656,8 +847,11 @@ impl ShapeExtent {
compat_mode: CompatMode)
-> Result<Self, ParseError<'i>> {
match Self::parse(input)? {
- ShapeExtent::Contain | ShapeExtent::Cover if compat_mode == CompatMode::Modern =>
- Err(StyleParseError::UnspecifiedError.into()),
+ ShapeExtent::Contain | ShapeExtent::Cover if compat_mode == CompatMode::Modern => {
+ Err(StyleParseError::UnspecifiedError.into())
+ },
+ ShapeExtent::Contain => Ok(ShapeExtent::ClosestSide),
+ ShapeExtent::Cover => Ok(ShapeExtent::FarthestCorner),
keyword => Ok(keyword),
}
}
diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs
index 4353a0fafa5..2b2c995082a 100644
--- a/components/style/values/specified/position.rs
+++ b/components/style/values/specified/position.rs
@@ -15,6 +15,7 @@ use values::computed::{CalcLengthOrPercentage, LengthOrPercentage as ComputedLen
use values::computed::{Context, ToComputedValue};
use values::generics::position::Position as GenericPosition;
use values::specified::{AllowQuirks, LengthOrPercentage, Percentage};
+use values::specified::transform::OriginComponent;
/// The specified value of a CSS `<position>`
pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
@@ -63,7 +64,8 @@ impl Position {
-> Result<Self, ParseError<'i>> {
match input.try(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) {
Ok(x_pos @ PositionComponent::Center) => {
- if let Ok(y_pos) = input.try(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) {
+ if let Ok(y_pos) = input.try(|i|
+ PositionComponent::parse_quirky(context, i, allow_quirks)) {
return Ok(Self::new(x_pos, y_pos));
}
let x_pos = input
@@ -110,7 +112,7 @@ impl Position {
let x_lop = i.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok();
let x_pos = PositionComponent::Side(x_keyword, x_lop);
return Ok((y_lop, x_pos));
- }
+ };
i.expect_ident_matching("center")?;
let x_pos = PositionComponent::Center;
Ok((y_lop, x_pos))
@@ -274,3 +276,108 @@ impl Side for Y {
*self == Y::Top
}
}
+
+/// The specified value of a legacy CSS `<position>`
+/// Modern position syntax supports 3 and 4-value syntax. That means:
+/// If three or four values are given, then each <percentage> or <length> represents an offset
+/// and must be preceded by a keyword, which specifies from which edge the offset is given.
+/// For example, `bottom 10px right 20px` represents a `10px` vertical
+/// offset up from the bottom edge and a `20px` horizontal offset leftward from the right edge.
+/// If three values are given, the missing offset is assumed to be zero.
+/// But for some historical reasons we need to keep CSS Level 2 syntax which only supports up to
+/// 2-value. This type represents this 2-value syntax.
+pub type LegacyPosition = GenericPosition<LegacyHPosition, LegacyVPosition>;
+
+/// The specified value of a horizontal position.
+pub type LegacyHPosition = OriginComponent<X>;
+
+/// The specified value of a vertical position.
+pub type LegacyVPosition = OriginComponent<Y>;
+
+impl Parse for LegacyPosition {
+ fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+ Self::parse_quirky(context, input, AllowQuirks::No)
+ }
+}
+
+impl LegacyPosition {
+ /// Parses a `<position>`, with quirks.
+ pub fn parse_quirky<'i, 't>(context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ allow_quirks: AllowQuirks)
+ -> Result<Self, ParseError<'i>> {
+ match input.try(|i| OriginComponent::parse(context, i)) {
+ Ok(x_pos @ OriginComponent::Center) => {
+ if let Ok(y_pos) = input.try(|i|
+ OriginComponent::parse(context, i)) {
+ return Ok(Self::new(x_pos, y_pos));
+ }
+ let x_pos = input
+ .try(|i| OriginComponent::parse(context, i))
+ .unwrap_or(x_pos);
+ let y_pos = OriginComponent::Center;
+ return Ok(Self::new(x_pos, y_pos));
+ },
+ Ok(OriginComponent::Side(x_keyword)) => {
+ if input.try(|i| i.expect_ident_matching("center")).is_ok() {
+ let x_pos = OriginComponent::Side(x_keyword);
+ let y_pos = OriginComponent::Center;
+ return Ok(Self::new(x_pos, y_pos));
+ }
+ if let Ok(y_keyword) = input.try(Y::parse) {
+ let x_pos = OriginComponent::Side(x_keyword);
+ let y_pos = OriginComponent::Side(y_keyword);
+ return Ok(Self::new(x_pos, y_pos));
+ }
+ let x_pos = OriginComponent::Side(x_keyword);
+ if let Ok(y_lop) = input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)) {
+ return Ok(Self::new(x_pos, OriginComponent::Length(y_lop)))
+ }
+ },
+ Ok(x_pos @ OriginComponent::Length(_)) => {
+ if let Ok(y_keyword) = input.try(Y::parse) {
+ let y_pos = OriginComponent::Side(y_keyword);
+ return Ok(Self::new(x_pos, y_pos));
+ }
+ if let Ok(y_lop) = input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)) {
+ let y_pos = OriginComponent::Length(y_lop);
+ return Ok(Self::new(x_pos, y_pos));
+ }
+ let y_pos = OriginComponent::Center;
+ let _ = input.try(|i| i.expect_ident_matching("center"));
+ return Ok(Self::new(x_pos, y_pos));
+ },
+ Err(_) => {},
+ }
+ let y_keyword = Y::parse(input)?;
+ let x_pos: Result<_, ParseError> = input.try(|i| {
+ if let Ok(x_keyword) = i.try(X::parse) {
+ let x_pos = OriginComponent::Side(x_keyword);
+ return Ok(x_pos);
+ }
+ i.expect_ident_matching("center")?;
+ Ok(OriginComponent::Center)
+ });
+ if let Ok(x_pos) = x_pos {
+ let y_pos = OriginComponent::Side(y_keyword);
+ return Ok(Self::new(x_pos, y_pos));
+ }
+ let x_pos = OriginComponent::Center;
+ let y_pos = OriginComponent::Side(y_keyword);
+ Ok(Self::new(x_pos, y_pos))
+ }
+
+ /// `center center`
+ #[inline]
+ pub fn center() -> Self {
+ Self::new(OriginComponent::Center, OriginComponent::Center)
+ }
+}
+
+impl ToCss for LegacyPosition {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ self.horizontal.to_css(dest)?;
+ dest.write_str(" ")?;
+ self.vertical.to_css(dest)
+ }
+}
diff --git a/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs
index e4f709ecab9..578a9b90ae3 100644
--- a/components/style/values/specified/transform.rs
+++ b/components/style/values/specified/transform.rs
@@ -124,6 +124,13 @@ impl<S> ToComputedValue for OriginComponent<S>
}
}
+impl<S> OriginComponent<S> {
+ /// `0%`
+ pub fn zero() -> Self {
+ OriginComponent::Length(LengthOrPercentage::Percentage(Percentage(0.)))
+ }
+}
+
impl Parse for TimingFunction {
fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
if let Ok(keyword) = input.try(TimingKeyword::parse) {