diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 0604b059263..501ef2948c9 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -147,23 +147,12 @@ impl ToCss for Gradient { impl Gradient { /// Parses a gradient from the given arguments. pub fn parse_function(context: &ParserContext, input: &mut Parser) -> Result { - let parse_linear_gradient = |input: &mut Parser, mode| { + fn parse(context: &ParserContext, input: &mut Parser, parse_kind: F) + -> Result<(GradientKind, Vec), ()> + where F: FnOnce(&ParserContext, &mut Parser) -> Result + { input.parse_nested_block(|input| { - let kind = try!(GradientKind::parse_linear(context, input, mode)); - let stops = try!(input.parse_comma_separated(|i| ColorStop::parse(context, i))); - Ok((kind, stops)) - }) - }; - let parse_modern_radial_gradient = |input: &mut Parser| { - input.parse_nested_block(|input| { - let kind = try!(GradientKind::parse_modern_radial(context, input)); - let stops = try!(input.parse_comma_separated(|i| ColorStop::parse(context, i))); - Ok((kind, stops)) - }) - }; - let parse_webkit_radial_gradient = |input: &mut Parser| { - input.parse_nested_block(|input| { - let kind = try!(GradientKind::parse_webkit_radial(context, input)); + let kind = try!(parse_kind(context, input)); let stops = try!(input.parse_comma_separated(|i| ColorStop::parse(context, i))); Ok((kind, stops)) }) @@ -172,36 +161,36 @@ impl Gradient { let mut compat_mode = CompatMode::Modern; let (gradient_kind, stops) = match_ignore_ascii_case! { &try!(input.expect_function()), "linear-gradient" => { - try!(parse_linear_gradient(input, compat_mode)) + try!(parse(context, input, GradientKind::parse_modern_linear)) }, "-webkit-linear-gradient" => { compat_mode = CompatMode::WebKit; - try!(parse_linear_gradient(input, compat_mode)) + try!(parse(context, input, GradientKind::parse_webkit_linear)) }, "repeating-linear-gradient" => { repeating = true; - try!(parse_linear_gradient(input, compat_mode)) + try!(parse(context, input, GradientKind::parse_modern_linear)) }, "-webkit-repeating-linear-gradient" => { repeating = true; compat_mode = CompatMode::WebKit; - try!(parse_linear_gradient(input, compat_mode)) + try!(parse(context, input, GradientKind::parse_webkit_linear)) }, "radial-gradient" => { - try!(parse_modern_radial_gradient(input)) + try!(parse(context, input, GradientKind::parse_modern_radial)) }, "-webkit-radial-gradient" => { compat_mode = CompatMode::WebKit; - try!(parse_webkit_radial_gradient(input)) + try!(parse(context, input, GradientKind::parse_webkit_radial)) }, "repeating-radial-gradient" => { repeating = true; - try!(parse_modern_radial_gradient(input)) + try!(parse(context, input, GradientKind::parse_modern_radial)) }, "-webkit-repeating-radial-gradient" => { repeating = true; compat_mode = CompatMode::WebKit; - try!(parse_webkit_radial_gradient(input)) + try!(parse(context, input, GradientKind::parse_webkit_radial)) }, _ => { return Err(()); } }; @@ -248,26 +237,95 @@ pub enum CompatMode { impl GradientKind { /// Parses a linear gradient kind from the given arguments. - fn parse_linear(context: &ParserContext, input: &mut Parser, mode: CompatMode) -> Result { - let angle_or_corner = try!(AngleOrCorner::parse(context, input, mode)); - Ok(GradientKind::Linear(angle_or_corner)) + fn parse_modern_linear(context: &ParserContext, input: &mut Parser) -> Result { + let direction = if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) { + try!(input.expect_comma()); + AngleOrCorner::Angle(angle) + } else { + if input.try(|i| i.expect_ident_matching("to")).is_ok() { + let (horizontal, vertical) = + if let Ok(value) = input.try(HorizontalDirection::parse) { + (Some(value), input.try(VerticalDirection::parse).ok()) + } else { + let value = try!(VerticalDirection::parse(input)); + (input.try(HorizontalDirection::parse).ok(), Some(value)) + }; + try!(input.expect_comma()); + AngleOrCorner::Corner(horizontal, vertical) + } else { + AngleOrCorner::None + } + }; + Ok(GradientKind::Linear(direction)) + } + + fn parse_webkit_linear(context: &ParserContext, input: &mut Parser) -> Result { + let direction = if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) { + AngleOrCorner::Angle(angle) + } else { + if let Ok(value) = input.try(HorizontalDirection::parse) { + AngleOrCorner::Corner(Some(value), input.try(VerticalDirection::parse).ok()) + } else { + if let Ok(value) = input.try(VerticalDirection::parse) { + AngleOrCorner::Corner(input.try(HorizontalDirection::parse).ok(), Some(value)) + } else { + AngleOrCorner::None + } + } + }; + if direction != AngleOrCorner::None { + try!(input.expect_comma()); + } + Ok(GradientKind::Linear(direction)) } /// Parses a modern radial gradient from the given arguments. pub fn parse_modern_radial(context: &ParserContext, input: &mut Parser) -> Result { let mut needs_comma = true; + // Ending shape and position can be in various order. Checks all probabilities. let (shape, position) = if let Ok(position) = input.try(|i| parse_position(context, i)) { - // Handle just "at" + // Handle just (EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), position) - } else if let Ok(shape) = input.try(|i| parse_shape(context, i, SizeKeyword::parse_modern)) { - // Handle ["at" ]? + } else if let Ok((first, second)) = input.try(|i| parse_two_length(context, i)) { + // Handle ? ? + let _ = input.try(|input| input.expect_ident_matching("ellipse")); + (EndingShape::Ellipse(LengthOrPercentageOrKeyword::LengthOrPercentage(first, second)), + input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) + } else if let Ok(length) = input.try(|i| Length::parse(context, i)) { + // Handle ? ? + let _ = input.try(|input| input.expect_ident_matching("circle")); + (EndingShape::Circle(LengthOrKeyword::Length(length)), + input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) + } else if let Ok(keyword) = input.try(SizeKeyword::parse_modern) { + // Handle ? ? + let shape = if input.try(|input| input.expect_ident_matching("circle")).is_ok() { + EndingShape::Circle(LengthOrKeyword::Keyword(keyword)) + } else { + let _ = input.try(|input| input.expect_ident_matching("ellipse")); + EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword)) + }; (shape, input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) } else { - // If there is no shape keyword, it should set to default. - needs_comma = false; - (EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), - Position::center()) + // Handle ? ? + if input.try(|input| input.expect_ident_matching("ellipse")).is_ok() { + // Handle ? ? + let length = input.try(|i| LengthOrPercentageOrKeyword::parse(context, i, SizeKeyword::parse_modern)) + .unwrap_or(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)); + (EndingShape::Ellipse(length), + input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) + } else if input.try(|input| input.expect_ident_matching("circle")).is_ok() { + // Handle ? ? + let length = input.try(|i| LengthOrKeyword::parse(context, i, SizeKeyword::parse_modern)) + .unwrap_or(LengthOrKeyword::Keyword(SizeKeyword::FarthestCorner)); + (EndingShape::Circle(length), input.try(|i| parse_position(context, i)) + .unwrap_or(Position::center())) + } else { + // If there is no shape keyword, it should set to default. + needs_comma = false; + (EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), + input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) + } }; if needs_comma { @@ -287,13 +345,40 @@ impl GradientKind { Position::center() }; - let shape = if let Ok(shape) = input.try(|i| parse_shape(context, i, SizeKeyword::parse)) { - try!(input.expect_comma()); - shape + let mut needs_comma = true; + + // Ending shape and position can be in various order. Checks all probabilities. + let shape = if let Ok((first, second)) = input.try(|i| parse_two_length(context, i)) { + EndingShape::Ellipse(LengthOrPercentageOrKeyword::LengthOrPercentage(first, second)) + } else if let Ok(keyword) = input.try(SizeKeyword::parse) { + // Handle ? + if input.try(|input| input.expect_ident_matching("circle")).is_ok() { + EndingShape::Circle(LengthOrKeyword::Keyword(keyword)) + } else { + let _ = input.try(|input| input.expect_ident_matching("ellipse")); + EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword)) + } } else { - EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::Cover)) + // Handle ? + if input.try(|input| input.expect_ident_matching("ellipse")).is_ok() { + // Handle ? + let keyword = input.try(SizeKeyword::parse).unwrap_or((SizeKeyword::Cover)); + EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword)) + } else if input.try(|input| input.expect_ident_matching("circle")).is_ok() { + // Handle ? + let keyword = input.try(SizeKeyword::parse).unwrap_or((SizeKeyword::Cover)); + EndingShape::Circle(LengthOrKeyword::Keyword(keyword)) + } else { + // If there is no shape keyword, it should set to default. + needs_comma = false; + EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::Cover)) + } }; + if needs_comma { + try!(input.expect_comma()); + } + Ok(GradientKind::Radial(shape, position)) } } @@ -368,46 +453,6 @@ fn parse_position(context: &ParserContext, input: &mut Parser) -> Result(context: &ParserContext, - input: &mut Parser, - parse_size_keyword: F) - -> Result - where F: FnOnce(&mut Parser) -> Result -{ - if let Ok((first, second)) = input.try(|i| parse_two_length(context, i)) { - // Handle ? - let _ = input.try(|input| input.expect_ident_matching("ellipse")); - Ok(EndingShape::Ellipse(LengthOrPercentageOrKeyword::LengthOrPercentage(first, second))) - } else if let Ok(length) = input.try(|i| Length::parse(context, i)) { - // Handle ? - let _ = input.try(|input| input.expect_ident_matching("circle")); - Ok(EndingShape::Circle(LengthOrKeyword::Length(length))) - } else if let Ok(keyword) = input.try(parse_size_keyword) { - // Handle ? - if input.try(|input| input.expect_ident_matching("circle")).is_ok() { - Ok(EndingShape::Circle(LengthOrKeyword::Keyword(keyword))) - } else { - let _ = input.try(|input| input.expect_ident_matching("ellipse")); - Ok(EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword))) - } - } else { - // https://github.com/rust-lang/rust/issues/41272 - if input.try(|input| input.expect_ident_matching("ellipse")).is_ok() { - // Handle ? - let length = input.try(|i| LengthOrPercentageOrKeyword::parse(context, i)) - .unwrap_or(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)); - Ok(EndingShape::Ellipse(length)) - } else if input.try(|input| input.expect_ident_matching("circle")).is_ok() { - // Handle ? - let length = input.try(|i| LengthOrKeyword::parse(context, i)) - .unwrap_or(LengthOrKeyword::Keyword(SizeKeyword::FarthestCorner)); - Ok(EndingShape::Circle(length)) - } else { - Err(()) - } - } -} - /// Specified values for an angle or a corner in a linear gradient. #[derive(Clone, PartialEq, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -444,28 +489,6 @@ impl AngleOrCorner { } } -impl AngleOrCorner { - fn parse(context: &ParserContext, input: &mut Parser, mode: CompatMode) -> Result { - if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) { - try!(input.expect_comma()); - return Ok(AngleOrCorner::Angle(angle)) - } - if mode == CompatMode::WebKit || input.try(|input| input.expect_ident_matching("to")).is_ok() { - let (horizontal, vertical) = - if let Ok(value) = input.try(HorizontalDirection::parse) { - (Some(value), input.try(VerticalDirection::parse).ok()) - } else { - let value = try!(VerticalDirection::parse(input)); - (input.try(HorizontalDirection::parse).ok(), Some(value)) - }; - try!(input.expect_comma()); - Ok(AngleOrCorner::Corner(horizontal, vertical)) - } else { - Ok(AngleOrCorner::None) - } - } -} - /// Specified values for one color stop in a linear gradient. /// https://drafts.csswg.org/css-images/#typedef-color-stop-list #[derive(Clone, PartialEq, Debug)] @@ -539,9 +562,11 @@ pub enum LengthOrKeyword { Keyword(SizeKeyword), } -impl Parse for LengthOrKeyword { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - if let Ok(keyword) = input.try(SizeKeyword::parse) { +impl LengthOrKeyword { + fn parse(context: &ParserContext, input: &mut Parser, parse_size_keyword: F) -> Result + where F: Fn(&mut Parser) -> Result + { + if let Ok(keyword) = input.try(parse_size_keyword) { Ok(LengthOrKeyword::Keyword(keyword)) } else { Ok(LengthOrKeyword::Length(try!(Length::parse(context, input)))) @@ -568,9 +593,11 @@ pub enum LengthOrPercentageOrKeyword { } -impl Parse for LengthOrPercentageOrKeyword { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - if let Ok(keyword) = input.try(SizeKeyword::parse) { +impl LengthOrPercentageOrKeyword { + fn parse(context: &ParserContext, input: &mut Parser, parse_size_keyword: F) -> Result + where F: Fn(&mut Parser) -> Result + { + if let Ok(keyword) = input.try(parse_size_keyword) { Ok(LengthOrPercentageOrKeyword::Keyword(keyword)) } else { Ok(LengthOrPercentageOrKeyword::LengthOrPercentage(