From 3e42684d3cab92d1ad5de47a85c2176c819541f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Tue, 20 Jun 2017 10:56:57 -0700 Subject: [PATCH 1/3] stylo: Implement -moz-prefixed linear gradients --- components/style/gecko/conversions.rs | 25 +++++- components/style/gecko/generated/bindings.rs | 4 +- .../properties/shorthand/background.mako.rs | 4 +- components/style/values/computed/image.rs | 34 ++++++- components/style/values/generics/image.rs | 11 ++- components/style/values/specified/image.rs | 89 ++++++++++++++++--- components/style/values/specified/position.rs | 74 ++++++++++++--- 7 files changed, 207 insertions(+), 34 deletions(-) diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index ac87b180953..95f4563b538 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,7 +235,24 @@ 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 }, @@ -274,7 +292,8 @@ 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) }; 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/properties/shorthand/background.mako.rs b/components/style/properties/shorthand/background.mako.rs index 144dc829e77..9c4cfaf762d 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; + use values::specified::position::{Position, PositionSyntax}; 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)?; + let value = Position::parse_quirky(context, input, AllowQuirks::Yes, PositionSyntax::Modern)?; position_x.0.push(value.horizontal); position_y.0.push(value.vertical); any = true; diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index 05640c94f6f..bacb912b567 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -56,6 +56,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, Option), } /// A computed radial gradient ending shape. @@ -75,6 +78,8 @@ impl GenericLineDirection for LineDirection { match *self { LineDirection::Angle(angle) => angle.radians() == PI, LineDirection::Corner(..) => false, + #[cfg(feature = "gecko")] + LineDirection::MozPosition(_, _) => false, } } @@ -91,6 +96,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 +122,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 +151,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 +167,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)) + }, } } } diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index 31a4606aa79..59128b057ec 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -50,12 +50,14 @@ pub struct Gradient #[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. @@ -216,9 +218,12 @@ impl ToCss for Gradient where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, { fn to_css(&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-")?; } diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 7fc73f94733..56d09f68317 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -68,6 +68,12 @@ 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, Option), } /// A specified ending shape. @@ -160,12 +166,20 @@ 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)) }, @@ -184,14 +198,14 @@ impl Parse for Gradient { _ => 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::Linear => GradientKind::parse_linear(context, i, &mut compat_mode)?, Shape::Radial => GradientKind::parse_radial(context, i, compat_mode)?, }; let items = GradientItem::parse_comma_separated(context, i)?; @@ -461,9 +475,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> { let direction = if let Ok(d) = input.try(|i| LineDirection::parse(context, i, compat_mode)) { input.expect_comma()?; @@ -543,7 +559,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 +582,51 @@ 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> { - 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")?; + 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| { + 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| Position::parse_legacy(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)); diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs index 4353a0fafa5..10418b05689 100644 --- a/components/style/values/specified/position.rs +++ b/components/style/values/specified/position.rs @@ -49,25 +49,46 @@ 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) + Self::parse_quirky(context, input, AllowQuirks::No, PositionSyntax::Modern) } } 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) + allow_quirks: AllowQuirks, + syntax: PositionSyntax) -> Result> { - match input.try(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) { + match input.try(|i| PositionComponent::parse_quirky(context, i, allow_quirks, syntax)) { 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, syntax)) { return Ok(Self::new(x_pos, y_pos)); } let x_pos = input - .try(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) + .try(|i| PositionComponent::parse_quirky(context, i, allow_quirks, syntax)) .unwrap_or(x_pos); let y_pos = PositionComponent::Center; return Ok(Self::new(x_pos, y_pos)); @@ -79,12 +100,22 @@ impl Position { return Ok(Self::new(x_pos, y_pos)); } if let Ok(y_keyword) = input.try(Y::parse) { - let y_lop = input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok(); + let y_lop = match syntax { + PositionSyntax::Modern => { + input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok() + }, + PositionSyntax::Legacy => None, + }; 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)); }, @@ -105,12 +136,22 @@ impl Position { } let y_keyword = Y::parse(input)?; let lop_and_x_pos: Result<_, ParseError> = input.try(|i| { - let y_lop = i.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok(); + let y_lop = match syntax { + PositionSyntax::Modern => { + i.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok() + }, + PositionSyntax::Legacy => None, + }; if let Ok(x_keyword) = i.try(X::parse) { - let x_lop = i.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok(); + let x_lop = match syntax { + PositionSyntax::Modern => { + i.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok() + }, + PositionSyntax::Legacy => None, + }; 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)) @@ -124,6 +165,13 @@ 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 { @@ -168,7 +216,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) + Self::parse_quirky(context, input, AllowQuirks::No, PositionSyntax::Modern) } } @@ -176,7 +224,8 @@ 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) + allow_quirks: AllowQuirks, + syntax: PositionSyntax) -> Result> { if input.try(|i| i.expect_ident_matching("center")).is_ok() { return Ok(PositionComponent::Center); @@ -185,6 +234,9 @@ 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)) } From d589645ee928996d11a1947d4d9364c3715178f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Thu, 22 Jun 2017 11:25:57 -0700 Subject: [PATCH 2/3] stylo: Implement -moz-prefixed radial gradients --- components/layout/display_list_builder.rs | 4 +- components/style/gecko/conversions.rs | 11 +- components/style/values/computed/image.rs | 15 ++- components/style/values/generics/image.rs | 20 ++-- components/style/values/specified/image.rs | 125 +++++++++++++++------ 5 files changed, 119 insertions(+), 56 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 95f4563b538..9b56f732835 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -256,7 +256,7 @@ impl nsStyleImage { } 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, @@ -297,9 +297,14 @@ impl nsStyleImage { 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/values/computed/image.rs b/components/style/values/computed/image.rs index bacb912b567..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. @@ -205,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))) } } } @@ -218,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 59128b057ec..78d011beac0 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -37,9 +37,9 @@ pub enum Image { /// https://drafts.csswg.org/css-images/#gradients #[derive(Clone, Debug, HasViewportPercentage, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct Gradient { +pub struct Gradient { /// Gradients can be linear or radial. - pub kind: GradientKind, + pub kind: GradientKind, /// The color stops and interpolation hints. pub items: Vec>, /// True if this is a repeating gradient. @@ -63,11 +63,11 @@ pub enum CompatMode { /// A gradient kind. #[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum GradientKind { +pub enum GradientKind { /// A linear gradient. Linear(LineDirection), /// A radial gradient. - Radial(EndingShape, Position), + Radial(EndingShape, Position, Option), } /// A radial gradient's ending shape. @@ -214,8 +214,8 @@ impl HasViewportPercentage for Image } } -impl ToCss for Gradient - where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, +impl ToCss for Gradient + where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, A: ToCss { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match self.compat_mode { @@ -235,7 +235,7 @@ impl ToCss for Gradient 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)) => { @@ -252,6 +252,10 @@ impl ToCss for Gradient 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)?; @@ -271,7 +275,7 @@ impl ToCss for Gradient } } -impl GradientKind { +impl GradientKind { 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 56d09f68317..a117d38f2e3 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -46,6 +46,7 @@ pub type Gradient = GenericGradient< LengthOrPercentage, Position, RGBAColor, + Angle, >; /// A specified gradient kind. @@ -54,6 +55,7 @@ pub type GradientKind = GenericGradientKind< Length, LengthOrPercentage, Position, + Angle, >; /// A specified gradient line direction. @@ -185,6 +187,10 @@ impl Parse for Gradient { }, "-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)) @@ -192,6 +198,10 @@ 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)); }, @@ -206,7 +216,7 @@ impl Parse for Gradient { let (kind, items) = input.parse_nested_block(|i| { let shape = match shape { 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)?; Ok((shape, items)) @@ -386,7 +396,7 @@ impl Gradient { let shape = GenericEndingShape::Circle(Circle::Radius(Length::from_px(radius.value))); let position = point.into(); - let kind = GenericGradientKind::Radial(shape, position); + let kind = GenericGradientKind::Radial(shape, position, None); (kind, reverse_stops) }, @@ -492,27 +502,56 @@ impl GradientKind { fn parse_radial<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>, - compat_mode: CompatMode) + compat_mode: &mut CompatMode) -> Result> { - 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) = 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) + }, + 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, None) + }, + // The syntax of `-moz-` prefixed radial gradient is: + // -moz-radial-gradient( + // [ [ || ]? [ ellipse | [ | ]{2} ] , | + // [ || ]? [ [ circle | ellipse ] | ] , | + // ]? + // [ , ]+ + // ) + // where = closest-corner | closest-side | farthest-corner | farthest-side | + // cover | contain + // and = [ | ]? + 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) - }); - (shape, position) + + let shape = input.try(|i| { + 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()?; } @@ -520,7 +559,11 @@ impl GradientKind { GenericEndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) }); 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))); } - 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 . + 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)?; @@ -723,8 +771,11 @@ impl ShapeExtent { compat_mode: CompatMode) -> Result> { 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), } } From b33545ca3ba37f0f37dae133ccf3164fbad8c64c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Fri, 23 Jun 2017 16:17:26 -0700 Subject: [PATCH 3/3] Move Position::parse_legacy logic to LegacyPosition type --- .../properties/shorthand/background.mako.rs | 4 +- components/style/values/specified/image.rs | 114 +++++++++-- components/style/values/specified/position.rs | 177 ++++++++++++------ .../style/values/specified/transform.rs | 7 + 4 files changed, 220 insertions(+), 82 deletions(-) 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) {