mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +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
|
@ -717,7 +717,7 @@ ${helpers.predefined_type("border-image-source", "ImageLayer",
|
|||
|
||||
let mut values = vec![];
|
||||
for _ in 0..4 {
|
||||
let value = input.try(|input| NumberOrPercentage::parse(context, input));
|
||||
let value = input.try(|input| NumberOrPercentage::parse_non_negative(context, input));
|
||||
match value {
|
||||
Ok(val) => values.push(val),
|
||||
Err(_) => break,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -15,7 +15,7 @@ use std::{cmp, fmt, mem};
|
|||
use std::ascii::AsciiExt;
|
||||
use std::ops::Mul;
|
||||
use style_traits::ToCss;
|
||||
use style_traits::values::specified::AllowedLengthType;
|
||||
use style_traits::values::specified::{AllowedLengthType, AllowedNumericType};
|
||||
use stylesheets::CssRuleType;
|
||||
use super::{AllowQuirks, Number, ToComputedValue};
|
||||
use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, HasViewportPercentage, None_, Normal};
|
||||
|
@ -729,7 +729,10 @@ impl ToCss for Percentage {
|
|||
}
|
||||
|
||||
impl Percentage {
|
||||
fn parse_internal(input: &mut Parser, context: AllowedLengthType) -> Result<Self, ()> {
|
||||
/// Parse a specific kind of percentage.
|
||||
pub fn parse_with_clamping_mode(input: &mut Parser,
|
||||
context: AllowedNumericType)
|
||||
-> Result<Self, ()> {
|
||||
match try!(input.next()) {
|
||||
Token::Percentage(ref value) if context.is_ok(value.unit_value) => {
|
||||
Ok(Percentage(value.unit_value))
|
||||
|
@ -740,14 +743,14 @@ impl Percentage {
|
|||
|
||||
/// Parses a percentage token, but rejects it if it's negative.
|
||||
pub fn parse_non_negative(input: &mut Parser) -> Result<Self, ()> {
|
||||
Self::parse_internal(input, AllowedLengthType::NonNegative)
|
||||
Self::parse_with_clamping_mode(input, AllowedNumericType::NonNegative)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Percentage {
|
||||
#[inline]
|
||||
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
Self::parse_internal(input, AllowedLengthType::All)
|
||||
Self::parse_with_clamping_mode(input, AllowedNumericType::All)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -658,7 +658,7 @@ impl ToCss for Number {
|
|||
|
||||
/// <number-percentage>
|
||||
/// Accepts only non-negative numbers.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum NumberOrPercentage {
|
||||
|
@ -668,13 +668,27 @@ pub enum NumberOrPercentage {
|
|||
|
||||
no_viewport_percentage!(NumberOrPercentage);
|
||||
|
||||
impl Parse for NumberOrPercentage {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
if let Ok(per) = input.try(Percentage::parse_non_negative) {
|
||||
impl NumberOrPercentage {
|
||||
fn parse_with_clamping_mode(context: &ParserContext,
|
||||
input: &mut Parser,
|
||||
type_: AllowedNumericType)
|
||||
-> Result<Self, ()> {
|
||||
if let Ok(per) = input.try(|i| Percentage::parse_with_clamping_mode(i, type_)) {
|
||||
return Ok(NumberOrPercentage::Percentage(per));
|
||||
}
|
||||
|
||||
Number::parse_non_negative(context, input).map(NumberOrPercentage::Number)
|
||||
parse_number_with_clamping_mode(context, input, type_).map(NumberOrPercentage::Number)
|
||||
}
|
||||
|
||||
/// Parse a non-negative number or percentage.
|
||||
pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for NumberOrPercentage {
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||
Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue