From 884b33eca61af16748f1a4a4cc07ec97b368d8f2 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 20 Apr 2017 04:27:33 -0500 Subject: [PATCH] servo: Merge #16511 - Implement -webkit-radial-gradient() (fixes #15441) (from nox:webkit-gradients); r=emilio Source-Repo: https://github.com/servo/servo Source-Revision: 7f825d2119a480a64b103e1d60a2d469af98d3de --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 117fc2f36804a6f9498add63c7556eba82b55409 --- servo/components/style/gecko/conversions.rs | 24 +-- .../style/values/specified/image.rs | 158 ++++++++++++------ 2 files changed, 121 insertions(+), 61 deletions(-) diff --git a/servo/components/style/gecko/conversions.rs b/servo/components/style/gecko/conversions.rs index 3531f35b9b25..0a14a5c34104 100644 --- a/servo/components/style/gecko/conversions.rs +++ b/servo/components/style/gecko/conversions.rs @@ -207,16 +207,21 @@ impl nsStyleImage { gecko_gradient }, GradientKind::Radial(shape, position) => { + let keyword_to_gecko_size = |keyword| { + match keyword { + SizeKeyword::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, + SizeKeyword::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, + SizeKeyword::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER, + SizeKeyword::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, + SizeKeyword::Contain => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, + SizeKeyword::Cover => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, + } + }; let (gecko_shape, gecko_size) = match shape { GradientShape::Circle(ref length) => { let size = match *length { LengthOrKeyword::Keyword(keyword) => { - match keyword { - SizeKeyword::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, - SizeKeyword::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, - SizeKeyword::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER, - SizeKeyword::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, - } + keyword_to_gecko_size(keyword) }, _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, }; @@ -225,12 +230,7 @@ impl nsStyleImage { GradientShape::Ellipse(ref length) => { let size = match *length { LengthOrPercentageOrKeyword::Keyword(keyword) => { - match keyword { - SizeKeyword::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, - SizeKeyword::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, - SizeKeyword::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER, - SizeKeyword::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, - } + keyword_to_gecko_size(keyword) }, _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, }; diff --git a/servo/components/style/values/specified/image.rs b/servo/components/style/values/specified/image.rs index 50129350c63f..0604b059263b 100644 --- a/servo/components/style/values/specified/image.rs +++ b/servo/components/style/values/specified/image.rs @@ -121,9 +121,15 @@ impl ToCss for Gradient { }, GradientKind::Radial(ref shape, ref position) => { try!(dest.write_str("radial-gradient(")); - try!(shape.to_css(dest)); - try!(dest.write_str(" at ")); - try!(position.to_css(dest)); + if self.compat_mode == CompatMode::Modern { + try!(shape.to_css(dest)); + try!(dest.write_str(" at ")); + try!(position.to_css(dest)); + } else { + try!(position.to_css(dest)); + try!(dest.write_str(", ")); + try!(shape.to_css(dest)); + } }, } for stop in &self.stops { @@ -148,9 +154,16 @@ impl Gradient { Ok((kind, stops)) }) }; - let parse_radial_gradient = |input: &mut Parser| { + let parse_modern_radial_gradient = |input: &mut Parser| { input.parse_nested_block(|input| { - let kind = try!(GradientKind::parse_radial(context, 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 stops = try!(input.parse_comma_separated(|i| ColorStop::parse(context, i))); Ok((kind, stops)) }) @@ -175,11 +188,20 @@ impl Gradient { try!(parse_linear_gradient(input, compat_mode)) }, "radial-gradient" => { - try!(parse_radial_gradient(input)) + try!(parse_modern_radial_gradient(input)) + }, + "-webkit-radial-gradient" => { + compat_mode = CompatMode::WebKit; + try!(parse_webkit_radial_gradient(input)) }, "repeating-radial-gradient" => { repeating = true; - try!(parse_radial_gradient(input)) + try!(parse_modern_radial_gradient(input)) + }, + "-webkit-repeating-radial-gradient" => { + repeating = true; + compat_mode = CompatMode::WebKit; + try!(parse_webkit_radial_gradient(input)) }, _ => { return Err(()); } }; @@ -231,53 +253,21 @@ impl GradientKind { Ok(GradientKind::Linear(angle_or_corner)) } - /// Parses a radial gradient from the given arguments. - pub fn parse_radial(context: &ParserContext, input: &mut Parser) -> Result { + /// 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 + // Handle just "at" (EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), position) - } 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) { - // 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)) - }; + } else if let Ok(shape) = input.try(|i| parse_shape(context, i, SizeKeyword::parse_modern)) { + // Handle ["at" ]? (shape, input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) } else { - // Handle ? ? - 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)); - (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)) - .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 there is no shape keyword, it should set to default. + needs_comma = false; + (EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), + Position::center()) }; if needs_comma { @@ -286,6 +276,26 @@ impl GradientKind { Ok(GradientKind::Radial(shape, position)) } + + /// Parses a webkit radial gradient from the given arguments. + /// https://compat.spec.whatwg.org/#css-gradients-webkit-radial-gradient + pub fn parse_webkit_radial(context: &ParserContext, input: &mut Parser) -> Result { + let position = if let Ok(position) = input.try(|i| Position::parse(context, i)) { + try!(input.expect_comma()); + position + } else { + Position::center() + }; + + let shape = if let Ok(shape) = input.try(|i| parse_shape(context, i, SizeKeyword::parse)) { + try!(input.expect_comma()); + shape + } else { + EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::Cover)) + }; + + Ok(GradientKind::Radial(shape, position)) + } } /// Specified values for `moz-image-rect` @@ -358,6 +368,46 @@ 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))] @@ -545,4 +595,14 @@ impl ToCss for LengthOrPercentageOrKeyword { /// https://drafts.csswg.org/css-images/#typedef-extent-keyword define_css_keyword_enum!(SizeKeyword: "closest-side" => ClosestSide, "farthest-side" => FarthestSide, - "closest-corner" => ClosestCorner, "farthest-corner" => FarthestCorner); + "closest-corner" => ClosestCorner, "farthest-corner" => FarthestCorner, + "contain" => Contain, "cover" => Cover); + +impl SizeKeyword { + fn parse_modern(input: &mut Parser) -> Result { + match try!(SizeKeyword::parse(input)) { + SizeKeyword::Contain | SizeKeyword::Cover => Err(()), + keyword => Ok(keyword), + } + } +}