diff --git a/components/style/properties/shorthand/background.mako.rs b/components/style/properties/shorthand/background.mako.rs index 9c4cfaf762d..144dc829e77 100644 --- a/components/style/properties/shorthand/background.mako.rs +++ b/components/style/properties/shorthand/background.mako.rs @@ -193,7 +193,7 @@ spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position"> use properties::longhands::{background_position_x, background_position_y}; use values::specified::AllowQuirks; - use values::specified::position::{Position, PositionSyntax}; + use values::specified::position::Position; pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { @@ -202,7 +202,7 @@ let mut any = false; input.parse_comma_separated(|input| { - let value = Position::parse_quirky(context, input, AllowQuirks::Yes, PositionSyntax::Modern)?; + let value = Position::parse_quirky(context, input, AllowQuirks::Yes)?; position_x.0.push(value.horizontal); position_y.0.push(value.vertical); any = true; diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index a117d38f2e3..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,6 +42,7 @@ pub type Image = GenericImage; /// Specified values for a CSS gradient. /// https://drafts.csswg.org/css-images/#gradients +#[cfg(not(feature = "gecko"))] pub type Gradient = GenericGradient< LineDirection, Length, @@ -49,7 +52,20 @@ pub type Gradient = GenericGradient< 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, @@ -58,6 +74,16 @@ pub type GradientKind = GenericGradientKind< Angle, >; +/// A specified gradient kind. +#[cfg(feature = "gecko")] +pub type GradientKind = GenericGradientKind< + LineDirection, + Length, + LengthOrPercentage, + GradientPosition, + Angle, +>; + /// A specified gradient line direction. #[derive(Clone, Debug, HasViewportPercentage, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -75,7 +101,17 @@ pub enum LineDirection { /// 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, Option), + MozPosition(Option, Option), +} + +/// 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 . + Modern(Position), + /// 1, 2-valued . + Legacy(LegacyPosition), } /// A specified ending shape. @@ -395,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, None); + 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()), }; @@ -504,14 +549,14 @@ impl GradientKind { input: &mut Parser<'i, 't>, compat_mode: &mut CompatMode) -> Result> { - let (shape, position, angle) = match *compat_mode { + 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, None) + (shape, position.ok(), None, None) }, CompatMode::WebKit => { let position = input.try(|i| Position::parse(context, i)); @@ -521,7 +566,7 @@ impl GradientKind { } EndingShape::parse(context, i, *compat_mode) }); - (shape, position, None) + (shape, position.ok(), None, None) }, // The syntax of `-moz-` prefixed radial gradient is: // -moz-radial-gradient( @@ -534,10 +579,10 @@ impl GradientKind { // cover | contain // and = [ | ]? CompatMode::Moz => { - let mut position = input.try(|i| Position::parse_legacy(context, i)); + 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| Position::parse_legacy(context, i)); + position = input.try(|i| LegacyPosition::parse(context, i)); } let shape = input.try(|i| { @@ -547,23 +592,39 @@ impl GradientKind { EndingShape::parse(context, i, *compat_mode) }); - (shape, position, angle) + (shape, None, angle, position.ok()) } }; - if shape.is_ok() || position.is_ok() || angle.is_some() { + 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)) }); - let position = position.unwrap_or(Position::center()); - // 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; + + #[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()); + #[cfg(feature = "gecko")] + { + return Ok(GenericGradientKind::Radial(shape, GradientPosition::Modern(position), angle)); + } + #[cfg(not(feature = "gecko"))] + { + return Ok(GenericGradientKind::Radial(shape, position, angle)); } - Ok(GenericGradientKind::Radial(shape, position, angle)) } } @@ -653,12 +714,11 @@ impl LineDirection { _ => {}, } - #[cfg(feature = "gecko")] { // `-moz-` prefixed linear gradient can be both Angle and Position. if *compat_mode == CompatMode::Moz { - let position = i.try(|i| Position::parse_legacy(context, i)).ok(); + let position = i.try(|i| LegacyPosition::parse(context, i)).ok(); if _angle.is_none() { _angle = i.try(|i| Angle::parse(context, i)).ok(); }; @@ -685,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>, diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs index 10418b05689..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 `` pub type Position = GenericPosition; @@ -49,46 +50,26 @@ define_css_keyword_enum! { Y: } add_impls_for_keyword_enum!(Y); -/// Modern position syntax supports 3 and 4-value syntax. That means: -/// If three or four values are given, then each or 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 enum represents whether position should parse modern syntax or legacy syntax. -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum PositionSyntax { - /// Modern syntax - Modern, - /// Legacy syntax - Legacy, -} - impl Parse for Position { fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { - Self::parse_quirky(context, input, AllowQuirks::No, PositionSyntax::Modern) + Self::parse_quirky(context, input, AllowQuirks::No) } } impl Position { /// Parses a ``, with quirks. - /// Syntax parameter uses PositionSyntax to determine whether the syntax parsing - /// mode is modern or legacy. pub fn parse_quirky<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>, - allow_quirks: AllowQuirks, - syntax: PositionSyntax) + allow_quirks: AllowQuirks) -> Result> { - match input.try(|i| PositionComponent::parse_quirky(context, i, allow_quirks, syntax)) { + 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, syntax)) { + PositionComponent::parse_quirky(context, i, allow_quirks)) { return Ok(Self::new(x_pos, y_pos)); } let x_pos = input - .try(|i| PositionComponent::parse_quirky(context, i, allow_quirks, syntax)) + .try(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) .unwrap_or(x_pos); let y_pos = PositionComponent::Center; return Ok(Self::new(x_pos, y_pos)); @@ -100,22 +81,12 @@ impl Position { return Ok(Self::new(x_pos, y_pos)); } if let Ok(y_keyword) = input.try(Y::parse) { - let y_lop = match syntax { - PositionSyntax::Modern => { - input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok() - }, - PositionSyntax::Legacy => None, - }; + let y_lop = input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok(); let x_pos = PositionComponent::Side(x_keyword, lop); let y_pos = PositionComponent::Side(y_keyword, y_lop); return Ok(Self::new(x_pos, y_pos)); } let x_pos = PositionComponent::Side(x_keyword, None); - if syntax == PositionSyntax::Legacy { - if let Ok(y_lop) = input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)) { - return Ok(Self::new(x_pos, PositionComponent::Length(y_lop))) - } - } let y_pos = lop.map_or(PositionComponent::Center, PositionComponent::Length); return Ok(Self::new(x_pos, y_pos)); }, @@ -136,19 +107,9 @@ impl Position { } let y_keyword = Y::parse(input)?; let lop_and_x_pos: Result<_, ParseError> = input.try(|i| { - let y_lop = match syntax { - PositionSyntax::Modern => { - i.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok() - }, - PositionSyntax::Legacy => None, - }; + let y_lop = i.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok(); if let Ok(x_keyword) = i.try(X::parse) { - let x_lop = match syntax { - PositionSyntax::Modern => { - i.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok() - }, - PositionSyntax::Legacy => None, - }; + 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)); }; @@ -165,13 +126,6 @@ impl Position { Ok(Self::new(x_pos, y_pos)) } - /// Parses legacy Position syntax. - pub fn parse_legacy<'i, 't>(context: &ParserContext, - input: &mut Parser<'i, 't>) - -> Result> { - Self::parse_quirky(context, input, AllowQuirks::No, PositionSyntax::Legacy) - } - /// `center center` #[inline] pub fn center() -> Self { @@ -216,7 +170,7 @@ impl HasViewportPercentage for PositionComponent { impl Parse for PositionComponent { fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { - Self::parse_quirky(context, input, AllowQuirks::No, PositionSyntax::Modern) + Self::parse_quirky(context, input, AllowQuirks::No) } } @@ -224,8 +178,7 @@ impl PositionComponent { /// Parses a component of a CSS position, with quirks. pub fn parse_quirky<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>, - allow_quirks: AllowQuirks, - syntax: PositionSyntax) + allow_quirks: AllowQuirks) -> Result> { if input.try(|i| i.expect_ident_matching("center")).is_ok() { return Ok(PositionComponent::Center); @@ -234,9 +187,6 @@ impl PositionComponent { return Ok(PositionComponent::Length(lop)); } let keyword = S::parse(context, input)?; - if syntax == PositionSyntax::Legacy { - return Ok(PositionComponent::Side(keyword, None)); - } let lop = input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok(); Ok(PositionComponent::Side(keyword, lop)) } @@ -326,3 +276,108 @@ impl Side for Y { *self == Y::Top } } + +/// The specified value of a legacy CSS `` +/// Modern position syntax supports 3 and 4-value syntax. That means: +/// If three or four values are given, then each or 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; + +/// The specified value of a horizontal position. +pub type LegacyHPosition = OriginComponent; + +/// The specified value of a vertical position. +pub type LegacyVPosition = OriginComponent; + +impl Parse for LegacyPosition { + fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { + Self::parse_quirky(context, input, AllowQuirks::No) + } +} + +impl LegacyPosition { + /// Parses a ``, with quirks. + pub fn parse_quirky<'i, 't>(context: &ParserContext, + input: &mut Parser<'i, 't>, + allow_quirks: AllowQuirks) + -> Result> { + 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(&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 ToComputedValue for OriginComponent } } +impl OriginComponent { + /// `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> { if let Ok(keyword) = input.try(TimingKeyword::parse) {