mirror of
https://github.com/servo/servo.git
synced 2025-08-07 14:35:33 +01:00
Auto merge of #16511 - nox:webkit-gradients, r=emilio
Implement -webkit-radial-gradient() (fixes #15441) <!-- Reviewable:start --> This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16511) <!-- Reviewable:end -->
This commit is contained in:
commit
7f825d2119
2 changed files with 121 additions and 61 deletions
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,53 +253,21 @@ 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;
|
||||||
|
|
||||||
// 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)) {
|
let (shape, position) = if let Ok(position) = input.try(|i| parse_position(context, i)) {
|
||||||
// Handle just <position>
|
// Handle just "at" <position>
|
||||||
(EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), position)
|
(EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), position)
|
||||||
} else if let Ok((first, second)) = input.try(|i| parse_two_length(context, i)) {
|
} else if let Ok(shape) = input.try(|i| parse_shape(context, i, SizeKeyword::parse_modern)) {
|
||||||
// Handle <LengthOrPercentage> <LengthOrPercentage> <shape>? <position>?
|
// Handle <shape> ["at" <position>]?
|
||||||
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 <Length> <circle>? <position>?
|
|
||||||
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 <keyword> <shape-keyword>? <position>?
|
|
||||||
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()))
|
(shape, input.try(|i| parse_position(context, i)).unwrap_or(Position::center()))
|
||||||
} else {
|
} else {
|
||||||
// Handle <shape-keyword> <length>? <position>?
|
// If there is no shape keyword, it should set to default.
|
||||||
if input.try(|input| input.expect_ident_matching("ellipse")).is_ok() {
|
needs_comma = false;
|
||||||
// Handle <ellipse> <LengthOrPercentageOrKeyword>? <position>?
|
(EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)),
|
||||||
let length = input.try(|i| LengthOrPercentageOrKeyword::parse(context, i))
|
Position::center())
|
||||||
.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 <ellipse> <LengthOrKeyword>? <position>?
|
|
||||||
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 needs_comma {
|
if needs_comma {
|
||||||
|
@ -286,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`
|
||||||
|
@ -358,6 +368,46 @@ 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<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)) {
|
||||||
|
// Handle <LengthOrPercentage> <LengthOrPercentage> <shape>?
|
||||||
|
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 <Length> <circle>?
|
||||||
|
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 <keyword> <shape-keyword>?
|
||||||
|
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 <ellipse> <LengthOrPercentageOrKeyword>?
|
||||||
|
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 <circle> <LengthOrKeyword>?
|
||||||
|
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.
|
/// Specified values for an angle or a corner in a linear gradient.
|
||||||
#[derive(Clone, PartialEq, Copy, Debug)]
|
#[derive(Clone, PartialEq, Copy, Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
@ -545,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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue