Implement -webkit-radial-gradient() (fixes #15441)

This commit is contained in:
Anthony Ramine 2017-04-18 11:00:25 +02:00
parent 50c25f3221
commit d5cd34dec8
2 changed files with 82 additions and 25 deletions

View file

@ -207,16 +207,21 @@ impl nsStyleImage {
gecko_gradient gecko_gradient
}, },
GradientKind::Radial(shape, position) => { 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 { let (gecko_shape, gecko_size) = match shape {
GradientShape::Circle(ref length) => { GradientShape::Circle(ref length) => {
let size = match *length { let size = match *length {
LengthOrKeyword::Keyword(keyword) => { LengthOrKeyword::Keyword(keyword) => {
match keyword { keyword_to_gecko_size(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,
}
}, },
_ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE,
}; };
@ -225,12 +230,7 @@ impl nsStyleImage {
GradientShape::Ellipse(ref length) => { GradientShape::Ellipse(ref length) => {
let size = match *length { let size = match *length {
LengthOrPercentageOrKeyword::Keyword(keyword) => { LengthOrPercentageOrKeyword::Keyword(keyword) => {
match keyword { keyword_to_gecko_size(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,
}
}, },
_ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE,
}; };

View file

@ -121,9 +121,15 @@ impl ToCss for Gradient {
}, },
GradientKind::Radial(ref shape, ref position) => { GradientKind::Radial(ref shape, ref position) => {
try!(dest.write_str("radial-gradient(")); try!(dest.write_str("radial-gradient("));
try!(shape.to_css(dest)); if self.compat_mode == CompatMode::Modern {
try!(dest.write_str(" at ")); try!(shape.to_css(dest));
try!(position.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 { for stop in &self.stops {
@ -148,9 +154,16 @@ impl Gradient {
Ok((kind, stops)) Ok((kind, stops))
}) })
}; };
let parse_radial_gradient = |input: &mut Parser| { let parse_modern_radial_gradient = |input: &mut Parser| {
input.parse_nested_block(|input| { 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))); let stops = try!(input.parse_comma_separated(|i| ColorStop::parse(context, i)));
Ok((kind, stops)) Ok((kind, stops))
}) })
@ -175,11 +188,20 @@ impl Gradient {
try!(parse_linear_gradient(input, compat_mode)) try!(parse_linear_gradient(input, compat_mode))
}, },
"radial-gradient" => { "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-radial-gradient" => {
repeating = true; 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(()); } _ => { return Err(()); }
}; };
@ -231,14 +253,14 @@ impl GradientKind {
Ok(GradientKind::Linear(angle_or_corner)) Ok(GradientKind::Linear(angle_or_corner))
} }
/// Parses a radial gradient from the given arguments. /// Parses a modern radial gradient from the given arguments.
pub fn parse_radial(context: &ParserContext, input: &mut Parser) -> Result<GradientKind, ()> { pub fn parse_modern_radial(context: &ParserContext, input: &mut Parser) -> Result<GradientKind, ()> {
let mut needs_comma = true; let mut needs_comma = true;
let (shape, position) = if let Ok(position) = input.try(|i| parse_position(context, i)) { let (shape, position) = if let Ok(position) = input.try(|i| parse_position(context, i)) {
// Handle just "at" <position> // Handle just "at" <position>
(EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), position) (EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), position)
} else if let Ok(shape) = input.try(|i| parse_shape(context, i)) { } else if let Ok(shape) = input.try(|i| parse_shape(context, i, SizeKeyword::parse_modern)) {
// Handle <shape> ["at" <position>]? // Handle <shape> ["at" <position>]?
(shape, input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) (shape, input.try(|i| parse_position(context, i)).unwrap_or(Position::center()))
} else { } else {
@ -254,6 +276,26 @@ impl GradientKind {
Ok(GradientKind::Radial(shape, position)) 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<GradientKind, ()> {
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` /// Specified values for `moz-image-rect`
@ -326,7 +368,12 @@ fn parse_position(context: &ParserContext, input: &mut Parser) -> Result<Positio
input.try(|i| Position::parse(context, i)) input.try(|i| Position::parse(context, i))
} }
fn parse_shape(context: &ParserContext, input: &mut Parser) -> Result<EndingShape, ()> { fn parse_shape<F>(context: &ParserContext,
input: &mut Parser,
parse_size_keyword: F)
-> Result<EndingShape, ()>
where F: FnOnce(&mut Parser) -> Result<SizeKeyword, ()>
{
if let Ok((first, second)) = input.try(|i| parse_two_length(context, i)) { if let Ok((first, second)) = input.try(|i| parse_two_length(context, i)) {
// Handle <LengthOrPercentage> <LengthOrPercentage> <shape>? // Handle <LengthOrPercentage> <LengthOrPercentage> <shape>?
let _ = input.try(|input| input.expect_ident_matching("ellipse")); let _ = input.try(|input| input.expect_ident_matching("ellipse"));
@ -335,7 +382,7 @@ fn parse_shape(context: &ParserContext, input: &mut Parser) -> Result<EndingShap
// Handle <Length> <circle>? // Handle <Length> <circle>?
let _ = input.try(|input| input.expect_ident_matching("circle")); let _ = input.try(|input| input.expect_ident_matching("circle"));
Ok(EndingShape::Circle(LengthOrKeyword::Length(length))) Ok(EndingShape::Circle(LengthOrKeyword::Length(length)))
} else if let Ok(keyword) = input.try(SizeKeyword::parse) { } else if let Ok(keyword) = input.try(parse_size_keyword) {
// Handle <keyword> <shape-keyword>? // Handle <keyword> <shape-keyword>?
if input.try(|input| input.expect_ident_matching("circle")).is_ok() { if input.try(|input| input.expect_ident_matching("circle")).is_ok() {
Ok(EndingShape::Circle(LengthOrKeyword::Keyword(keyword))) Ok(EndingShape::Circle(LengthOrKeyword::Keyword(keyword)))
@ -548,4 +595,14 @@ impl ToCss for LengthOrPercentageOrKeyword {
/// https://drafts.csswg.org/css-images/#typedef-extent-keyword /// https://drafts.csswg.org/css-images/#typedef-extent-keyword
define_css_keyword_enum!(SizeKeyword: "closest-side" => ClosestSide, "farthest-side" => FarthestSide, 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<Self, ()> {
match try!(SizeKeyword::parse(input)) {
SizeKeyword::Contain | SizeKeyword::Cover => Err(()),
keyword => Ok(keyword),
}
}
}