mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Implement -webkit-gradient() (fixes #16542)
This commit is contained in:
parent
9e6f9db127
commit
ea4e7299d4
4 changed files with 285 additions and 16 deletions
|
@ -12,6 +12,7 @@ use cssparser::{Parser, Token};
|
|||
use parser::{Parse, ParserContext};
|
||||
#[cfg(feature = "servo")]
|
||||
use servo_url::ServoUrl;
|
||||
use std::cmp::Ordering;
|
||||
use std::f32::consts::PI;
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
|
@ -21,8 +22,10 @@ use values::generics::image::{EndingShape as GenericEndingShape, Gradient as Gen
|
|||
use values::generics::image::{GradientItem as GenericGradientItem, GradientKind as GenericGradientKind};
|
||||
use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect};
|
||||
use values::generics::image::{LineDirection as GenericsLineDirection, ShapeExtent};
|
||||
use values::specified::{Angle, CSSColor, Length, LengthOrPercentage, NumberOrPercentage, Percentage};
|
||||
use values::specified::position::{Position, X, Y};
|
||||
use values::generics::position::Position as GenericPosition;
|
||||
use values::specified::{Angle, CSSColor, Color, Length, LengthOrPercentage};
|
||||
use values::specified::{Number, NumberOrPercentage, Percentage};
|
||||
use values::specified::position::{Position, PositionComponent, Side, X, Y};
|
||||
use values::specified::url::SpecifiedUrl;
|
||||
|
||||
/// A specified image layer.
|
||||
|
@ -145,6 +148,9 @@ impl Parse for Gradient {
|
|||
"-webkit-repeating-radial-gradient" => {
|
||||
(Shape::Radial, true, CompatMode::WebKit)
|
||||
},
|
||||
"-webkit-gradient" => {
|
||||
return input.parse_nested_block(|i| Self::parse_webkit_gradient_argument(context, i));
|
||||
},
|
||||
_ => { return Err(()); }
|
||||
};
|
||||
|
||||
|
@ -170,6 +176,252 @@ impl Parse for Gradient {
|
|||
}
|
||||
}
|
||||
|
||||
impl Gradient {
|
||||
fn parse_webkit_gradient_argument(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
type Point = GenericPosition<Component<X>, Component<Y>>;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Component<S> {
|
||||
Center,
|
||||
Number(NumberOrPercentage),
|
||||
Side(S),
|
||||
}
|
||||
|
||||
impl LineDirection {
|
||||
fn from_points(first: Point, second: Point) -> Self {
|
||||
let h_ord = first.horizontal.partial_cmp(&second.horizontal);
|
||||
let v_ord = first.vertical.partial_cmp(&second.vertical);
|
||||
let (h, v) = match (h_ord, v_ord) {
|
||||
(Some(h), Some(v)) => (h, v),
|
||||
_ => return LineDirection::Vertical(Y::Bottom),
|
||||
};
|
||||
match (h, v) {
|
||||
(Ordering::Less, Ordering::Less) => {
|
||||
LineDirection::Corner(X::Right, Y::Bottom)
|
||||
},
|
||||
(Ordering::Less, Ordering::Equal) => {
|
||||
LineDirection::Horizontal(X::Right)
|
||||
},
|
||||
(Ordering::Less, Ordering::Greater) => {
|
||||
LineDirection::Corner(X::Right, Y::Top)
|
||||
},
|
||||
(Ordering::Equal, Ordering::Greater) => {
|
||||
LineDirection::Vertical(Y::Top)
|
||||
},
|
||||
(Ordering::Equal, Ordering::Equal) |
|
||||
(Ordering::Equal, Ordering::Less) => {
|
||||
LineDirection::Vertical(Y::Bottom)
|
||||
},
|
||||
(Ordering::Greater, Ordering::Less) => {
|
||||
LineDirection::Corner(X::Left, Y::Bottom)
|
||||
},
|
||||
(Ordering::Greater, Ordering::Equal) => {
|
||||
LineDirection::Horizontal(X::Left)
|
||||
},
|
||||
(Ordering::Greater, Ordering::Greater) => {
|
||||
LineDirection::Corner(X::Left, Y::Top)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Point> for Position {
|
||||
fn from(point: Point) -> Self {
|
||||
Self::new(point.horizontal.into(), point.vertical.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Point {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
input.try(|i| {
|
||||
let x = Component::parse(context, i)?;
|
||||
let y = Component::parse(context, i)?;
|
||||
|
||||
Ok(Self::new(x, y))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Side> From<Component<S>> for NumberOrPercentage {
|
||||
fn from(component: Component<S>) -> Self {
|
||||
match component {
|
||||
Component::Center => NumberOrPercentage::Percentage(Percentage(0.5)),
|
||||
Component::Number(number) => number,
|
||||
Component::Side(side) => {
|
||||
let p = Percentage(if side.is_start() { 0. } else { 1. });
|
||||
NumberOrPercentage::Percentage(p)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Side> From<Component<S>> for PositionComponent<S> {
|
||||
fn from(component: Component<S>) -> Self {
|
||||
match component {
|
||||
Component::Center => {
|
||||
PositionComponent::Center
|
||||
},
|
||||
Component::Number(NumberOrPercentage::Number(number)) => {
|
||||
PositionComponent::Length(Length::from_px(number.value).into())
|
||||
},
|
||||
Component::Number(NumberOrPercentage::Percentage(p)) => {
|
||||
PositionComponent::Length(p.into())
|
||||
},
|
||||
Component::Side(side) => {
|
||||
PositionComponent::Side(side, None)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Copy + Side> Component<S> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match (NumberOrPercentage::from(*self), NumberOrPercentage::from(*other)) {
|
||||
(NumberOrPercentage::Percentage(a), NumberOrPercentage::Percentage(b)) => {
|
||||
a.0.partial_cmp(&b.0)
|
||||
},
|
||||
(NumberOrPercentage::Number(a), NumberOrPercentage::Number(b)) => {
|
||||
a.value.partial_cmp(&b.value)
|
||||
},
|
||||
(_, _) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Parse> Parse for Component<S> {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
if let Ok(side) = input.try(|i| S::parse(context, i)) {
|
||||
return Ok(Component::Side(side));
|
||||
}
|
||||
if let Ok(number) = input.try(|i| NumberOrPercentage::parse(context, i)) {
|
||||
return Ok(Component::Number(number));
|
||||
}
|
||||
input.try(|i| i.expect_ident_matching("center"))?;
|
||||
Ok(Component::Center)
|
||||
}
|
||||
}
|
||||
|
||||
let ident = input.expect_ident()?;
|
||||
input.expect_comma()?;
|
||||
|
||||
let (kind, reverse_stops) = match_ignore_ascii_case! { &ident,
|
||||
"linear" => {
|
||||
let first = Point::parse(context, input)?;
|
||||
input.expect_comma()?;
|
||||
let second = Point::parse(context, input)?;
|
||||
|
||||
let direction = LineDirection::from_points(first, second);
|
||||
let kind = GenericGradientKind::Linear(direction);
|
||||
|
||||
(kind, false)
|
||||
},
|
||||
"radial" => {
|
||||
let first_point = Point::parse(context, input)?;
|
||||
input.expect_comma()?;
|
||||
let first_radius = Number::parse(context, input)?;
|
||||
input.expect_comma()?;
|
||||
let second_point = Point::parse(context, input)?;
|
||||
input.expect_comma()?;
|
||||
let second_radius = Number::parse(context, input)?;
|
||||
|
||||
let (reverse_stops, point, radius) = if second_radius.value >= first_radius.value {
|
||||
(false, second_point, second_radius)
|
||||
} else {
|
||||
(true, first_point, first_radius)
|
||||
};
|
||||
|
||||
let shape = GenericEndingShape::Circle(Circle::Radius(Length::from_px(radius.value)));
|
||||
let position = point.into();
|
||||
let kind = GenericGradientKind::Radial(shape, position);
|
||||
|
||||
(kind, reverse_stops)
|
||||
},
|
||||
_ => return Err(()),
|
||||
};
|
||||
|
||||
let mut items = input.try(|i| {
|
||||
i.expect_comma()?;
|
||||
i.parse_comma_separated(|i| {
|
||||
let function = i.expect_function()?;
|
||||
let (color, mut p) = i.parse_nested_block(|i| {
|
||||
let p = match_ignore_ascii_case! { &function,
|
||||
"color-stop" => {
|
||||
let p = match NumberOrPercentage::parse(context, i)? {
|
||||
NumberOrPercentage::Number(number) => number.value,
|
||||
NumberOrPercentage::Percentage(p) => p.0,
|
||||
};
|
||||
i.expect_comma()?;
|
||||
p
|
||||
},
|
||||
"from" => 0.,
|
||||
"to" => 1.,
|
||||
_ => return Err(()),
|
||||
};
|
||||
let color = CSSColor::parse(context, i)?;
|
||||
if color.parsed == Color::CurrentColor {
|
||||
return Err(());
|
||||
}
|
||||
Ok((color, p))
|
||||
})?;
|
||||
if reverse_stops {
|
||||
p = 1. - p;
|
||||
}
|
||||
Ok(GenericGradientItem::ColorStop(GenericColorStop {
|
||||
color: color,
|
||||
position: Some(LengthOrPercentage::Percentage(Percentage(p))),
|
||||
}))
|
||||
})
|
||||
}).unwrap_or(vec![]);
|
||||
|
||||
if items.is_empty() {
|
||||
items = vec![
|
||||
GenericGradientItem::ColorStop(GenericColorStop {
|
||||
color: CSSColor::transparent(),
|
||||
position: Some(Percentage(0.).into()),
|
||||
}),
|
||||
GenericGradientItem::ColorStop(GenericColorStop {
|
||||
color: CSSColor::transparent(),
|
||||
position: Some(Percentage(1.).into()),
|
||||
}),
|
||||
];
|
||||
} else if items.len() == 1 {
|
||||
let first = items[0].clone();
|
||||
items.push(first);
|
||||
} else {
|
||||
items.sort_by(|a, b| {
|
||||
match (a, b) {
|
||||
(&GenericGradientItem::ColorStop(ref a), &GenericGradientItem::ColorStop(ref b)) => {
|
||||
match (&a.position, &b.position) {
|
||||
(&Some(LengthOrPercentage::Percentage(a)), &Some(LengthOrPercentage::Percentage(b))) => {
|
||||
let ordering = a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal);
|
||||
if ordering != Ordering::Equal {
|
||||
return ordering;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
if reverse_stops {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Less
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Ok(GenericGradient {
|
||||
kind: kind,
|
||||
items: items,
|
||||
repeating: false,
|
||||
compat_mode: CompatMode::Modern,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GradientKind {
|
||||
fn parse_linear(context: &ParserContext,
|
||||
input: &mut Parser,
|
||||
|
@ -408,13 +660,13 @@ impl Parse for ImageRect {
|
|||
let string = i.expect_url_or_string()?;
|
||||
let url = SpecifiedUrl::parse_from_string(string, context)?;
|
||||
i.expect_comma()?;
|
||||
let top = NumberOrPercentage::parse(context, i)?;
|
||||
let top = NumberOrPercentage::parse_non_negative(context, i)?;
|
||||
i.expect_comma()?;
|
||||
let right = NumberOrPercentage::parse(context, i)?;
|
||||
let right = NumberOrPercentage::parse_non_negative(context, i)?;
|
||||
i.expect_comma()?;
|
||||
let bottom = NumberOrPercentage::parse(context, i)?;
|
||||
let bottom = NumberOrPercentage::parse_non_negative(context, i)?;
|
||||
i.expect_comma()?;
|
||||
let left = NumberOrPercentage::parse(context, i)?;
|
||||
let left = NumberOrPercentage::parse_non_negative(context, i)?;
|
||||
|
||||
Ok(ImageRect {
|
||||
url: url,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue