Implement webkit-prefixed linear gradients

This is half of https://github.com/servo/servo/issues/15441.
This commit is contained in:
Anthony Ramine 2017-04-13 18:37:46 +02:00
parent e01529a647
commit 133b599a6f
2 changed files with 70 additions and 28 deletions

View file

@ -15,6 +15,7 @@ use style_traits::ToCss;
use values::computed::{Angle, Context, Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue}; use values::computed::{Angle, Context, Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue};
use values::computed::position::Position; use values::computed::position::Position;
use values::specified::{self, HorizontalDirection, SizeKeyword, VerticalDirection}; use values::specified::{self, HorizontalDirection, SizeKeyword, VerticalDirection};
use values::specified::image::CompatMode;
use values::specified::url::SpecifiedUrl; use values::specified::url::SpecifiedUrl;
@ -124,17 +125,22 @@ pub struct Gradient {
pub repeating: bool, pub repeating: bool,
/// Gradient kind can be linear or radial. /// Gradient kind can be linear or radial.
pub gradient_kind: GradientKind, pub gradient_kind: GradientKind,
/// Compatibility mode.
pub compat_mode: CompatMode,
} }
impl ToCss for Gradient { impl ToCss for Gradient {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.compat_mode == CompatMode::WebKit {
try!(dest.write_str("-webkit-"));
}
if self.repeating { if self.repeating {
try!(dest.write_str("repeating-")); try!(dest.write_str("repeating-"));
} }
match self.gradient_kind { match self.gradient_kind {
GradientKind::Linear(angle_or_corner) => { GradientKind::Linear(angle_or_corner) => {
try!(dest.write_str("linear-gradient(")); try!(dest.write_str("linear-gradient("));
try!(angle_or_corner.to_css(dest)); try!(angle_or_corner.to_css(dest, self.compat_mode));
}, },
GradientKind::Radial(ref shape, position) => { GradientKind::Radial(ref shape, position) => {
try!(dest.write_str("radial-gradient(")); try!(dest.write_str("radial-gradient("));
@ -178,25 +184,30 @@ impl ToComputedValue for specified::Gradient {
let specified::Gradient { let specified::Gradient {
ref stops, ref stops,
repeating, repeating,
ref gradient_kind ref gradient_kind,
compat_mode,
} = *self; } = *self;
Gradient { Gradient {
stops: stops.iter().map(|s| s.to_computed_value(context)).collect(), stops: stops.iter().map(|s| s.to_computed_value(context)).collect(),
repeating: repeating, repeating: repeating,
gradient_kind: gradient_kind.to_computed_value(context), gradient_kind: gradient_kind.to_computed_value(context),
compat_mode: compat_mode,
} }
} }
#[inline] #[inline]
fn from_computed_value(computed: &Gradient) -> Self { fn from_computed_value(computed: &Gradient) -> Self {
let Gradient { let Gradient {
ref stops, ref stops,
repeating, repeating,
ref gradient_kind ref gradient_kind,
compat_mode,
} = *computed; } = *computed;
specified::Gradient { specified::Gradient {
stops: stops.iter().map(ToComputedValue::from_computed_value).collect(), stops: stops.iter().map(ToComputedValue::from_computed_value).collect(),
repeating: repeating, repeating: repeating,
gradient_kind: ToComputedValue::from_computed_value(gradient_kind), gradient_kind: ToComputedValue::from_computed_value(gradient_kind),
compat_mode: compat_mode,
} }
} }
} }
@ -602,12 +613,14 @@ impl ToComputedValue for specified::AngleOrCorner {
} }
} }
impl ToCss for AngleOrCorner { impl AngleOrCorner {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W, mode: CompatMode) -> fmt::Result where W: fmt::Write {
match *self { match *self {
AngleOrCorner::Angle(angle) => angle.to_css(dest), AngleOrCorner::Angle(angle) => angle.to_css(dest),
AngleOrCorner::Corner(horizontal, vertical) => { AngleOrCorner::Corner(horizontal, vertical) => {
try!(dest.write_str("to ")); if mode == CompatMode::Modern {
try!(dest.write_str("to "));
}
try!(horizontal.to_css(dest)); try!(horizontal.to_css(dest));
try!(dest.write_str(" ")); try!(dest.write_str(" "));
try!(vertical.to_css(dest)); try!(vertical.to_css(dest));

View file

@ -98,10 +98,15 @@ pub struct Gradient {
pub repeating: bool, pub repeating: bool,
/// Gradients can be linear or radial. /// Gradients can be linear or radial.
pub gradient_kind: GradientKind, pub gradient_kind: GradientKind,
/// Compatibility mode.
pub compat_mode: CompatMode,
} }
impl ToCss for Gradient { impl ToCss for Gradient {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.compat_mode == CompatMode::WebKit {
try!(dest.write_str("-webkit-"));
}
if self.repeating { if self.repeating {
try!(dest.write_str("repeating-")); try!(dest.write_str("repeating-"));
} }
@ -109,7 +114,7 @@ impl ToCss for Gradient {
match self.gradient_kind { match self.gradient_kind {
GradientKind::Linear(angle_or_corner) => { GradientKind::Linear(angle_or_corner) => {
try!(dest.write_str("linear-gradient(")); try!(dest.write_str("linear-gradient("));
try!(angle_or_corner.to_css(dest)); try!(angle_or_corner.to_css(dest, self.compat_mode));
if angle_or_corner == AngleOrCorner::None { if angle_or_corner == AngleOrCorner::None {
skipcomma = true; skipcomma = true;
} }
@ -136,9 +141,9 @@ impl ToCss for Gradient {
impl Gradient { impl Gradient {
/// Parses a gradient from the given arguments. /// Parses a gradient from the given arguments.
pub fn parse_function(context: &ParserContext, input: &mut Parser) -> Result<Gradient, ()> { pub fn parse_function(context: &ParserContext, input: &mut Parser) -> Result<Gradient, ()> {
let parse_linear_gradient = |input: &mut Parser| { let parse_linear_gradient = |input: &mut Parser, mode| {
input.parse_nested_block(|input| { input.parse_nested_block(|input| {
let kind = try!(GradientKind::parse_linear(context, input)); let kind = try!(GradientKind::parse_linear(context, input, mode));
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))
}) })
@ -151,13 +156,23 @@ impl Gradient {
}) })
}; };
let mut repeating = false; let mut repeating = false;
let mut compat_mode = CompatMode::Modern;
let (gradient_kind, stops) = match_ignore_ascii_case! { &try!(input.expect_function()), let (gradient_kind, stops) = match_ignore_ascii_case! { &try!(input.expect_function()),
"linear-gradient" => { "linear-gradient" => {
try!(parse_linear_gradient(input)) try!(parse_linear_gradient(input, compat_mode))
},
"-webkit-linear-gradient" => {
compat_mode = CompatMode::WebKit;
try!(parse_linear_gradient(input, compat_mode))
}, },
"repeating-linear-gradient" => { "repeating-linear-gradient" => {
repeating = true; repeating = true;
try!(parse_linear_gradient(input)) try!(parse_linear_gradient(input, compat_mode))
},
"-webkit-repeating-linear-gradient" => {
repeating = true;
compat_mode = CompatMode::WebKit;
try!(parse_linear_gradient(input, compat_mode))
}, },
"radial-gradient" => { "radial-gradient" => {
try!(parse_radial_gradient(input)) try!(parse_radial_gradient(input))
@ -178,6 +193,7 @@ impl Gradient {
stops: stops, stops: stops,
repeating: repeating, repeating: repeating,
gradient_kind: gradient_kind, gradient_kind: gradient_kind,
compat_mode: compat_mode,
}) })
} }
} }
@ -198,10 +214,20 @@ pub enum GradientKind {
Radial(EndingShape, Position), Radial(EndingShape, Position),
} }
#[derive(Clone, Copy, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// Whether we used the modern notation or the compatibility `-webkit` prefix.
pub enum CompatMode {
/// Modern syntax.
Modern,
/// `-webkit` prefix.
WebKit,
}
impl GradientKind { impl GradientKind {
/// Parses a linear gradient kind from the given arguments. /// Parses a linear gradient kind from the given arguments.
pub fn parse_linear(context: &ParserContext, input: &mut Parser) -> Result<GradientKind, ()> { fn parse_linear(context: &ParserContext, input: &mut Parser, mode: CompatMode) -> Result<GradientKind, ()> {
let angle_or_corner = try!(AngleOrCorner::parse(context, input)); let angle_or_corner = try!(AngleOrCorner::parse(context, input, mode));
Ok(GradientKind::Linear(angle_or_corner)) Ok(GradientKind::Linear(angle_or_corner))
} }
@ -342,13 +368,15 @@ pub enum AngleOrCorner {
None, None,
} }
impl ToCss for AngleOrCorner { impl AngleOrCorner {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result where W: fmt::Write {
match *self { match *self {
AngleOrCorner::None => Ok(()), AngleOrCorner::None => Ok(()),
AngleOrCorner::Angle(angle) => angle.to_css(dest), AngleOrCorner::Angle(angle) => angle.to_css(dest),
AngleOrCorner::Corner(horizontal, vertical) => { AngleOrCorner::Corner(horizontal, vertical) => {
try!(dest.write_str("to ")); if compat_mode == CompatMode::Modern {
try!(dest.write_str("to "));
}
let mut horizontal_present = false; let mut horizontal_present = false;
if let Some(horizontal) = horizontal { if let Some(horizontal) = horizontal {
try!(horizontal.to_css(dest)); try!(horizontal.to_css(dest));
@ -366,21 +394,22 @@ impl ToCss for AngleOrCorner {
} }
} }
impl Parse for AngleOrCorner { impl AngleOrCorner {
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { fn parse(context: &ParserContext, input: &mut Parser, mode: CompatMode) -> Result<Self, ()> {
if input.try(|input| input.expect_ident_matching("to")).is_ok() { if let Ok(angle) = input.try(|i| Angle::parse(context, i)) {
try!(input.expect_comma());
return Ok(AngleOrCorner::Angle(angle))
}
if mode == CompatMode::WebKit || input.try(|input| input.expect_ident_matching("to")).is_ok() {
let (horizontal, vertical) = let (horizontal, vertical) =
if let Ok(value) = input.try(HorizontalDirection::parse) { if let Ok(value) = input.try(HorizontalDirection::parse) {
(Some(value), input.try(VerticalDirection::parse).ok()) (Some(value), input.try(VerticalDirection::parse).ok())
} else { } else {
let value = try!(VerticalDirection::parse(input)); let value = try!(VerticalDirection::parse(input));
(input.try(HorizontalDirection::parse).ok(), Some(value)) (input.try(HorizontalDirection::parse).ok(), Some(value))
}; };
try!(input.expect_comma()); try!(input.expect_comma());
Ok(AngleOrCorner::Corner(horizontal, vertical)) Ok(AngleOrCorner::Corner(horizontal, vertical))
} else if let Ok(angle) = input.try(|i| Angle::parse(context, i)) {
try!(input.expect_comma());
Ok(AngleOrCorner::Angle(angle))
} else { } else {
Ok(AngleOrCorner::None) Ok(AngleOrCorner::None)
} }