Skip to content

Commit

Permalink
Auto merge of #16628 - nox:INTERPOL, r=emilio
Browse files Browse the repository at this point in the history
Parse interpolation hints (fixes #15166)

<!-- 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/16628)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo committed Apr 27, 2017
2 parents a6c3bc6 + ae82cda commit f598adc
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 57 deletions.
20 changes: 13 additions & 7 deletions components/layout/display_list_builder.rs
Expand Up @@ -58,8 +58,8 @@ use style::properties::longhands::border_image_repeat::computed_value::RepeatKey
use style::properties::style_structs;
use style::servo::restyle_damage::REPAINT;
use style::values::{Either, RGBA, computed};
use style::values::computed::{AngleOrCorner, Gradient, GradientKind, LengthOrPercentage};
use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage};
use style::values::computed::{AngleOrCorner, Gradient, GradientItem, GradientKind};
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, NumberOrPercentage};
use style::values::specified::{HorizontalDirection, VerticalDirection};
use style_traits::CSSPixel;
use style_traits::cursor::Cursor;
Expand Down Expand Up @@ -948,9 +948,15 @@ impl FragmentDisplayListBuilding for Fragment {
// Determine the position of each stop per CSS-IMAGES § 3.4.
//
// FIXME(#3908, pcwalton): Make sure later stops can't be behind earlier stops.
let mut stops = Vec::with_capacity(gradient.stops.len());
let stop_items = gradient.items.iter().filter_map(|item| {
match *item {
GradientItem::ColorStop(ref stop) => Some(stop),
_ => None,
}
}).collect::<Vec<_>>();
let mut stops = Vec::with_capacity(stop_items.len());
let mut stop_run = None;
for (i, stop) in gradient.stops.iter().enumerate() {
for (i, stop) in stop_items.iter().enumerate() {
let offset = match stop.position {
None => {
if stop_run.is_none() {
Expand All @@ -960,14 +966,14 @@ impl FragmentDisplayListBuilding for Fragment {
} else {
// `unwrap()` here should never fail because this is the beginning of
// a stop run, which is always bounded by a length or percentage.
position_to_offset(gradient.stops[i - 1].position.unwrap(), length)
position_to_offset(stop_items[i - 1].position.unwrap(), length)
};
let (end_index, end_offset) =
match gradient.stops[i..]
match stop_items[i..]
.iter()
.enumerate()
.find(|&(_, ref stop)| stop.position.is_some()) {
None => (gradient.stops.len() - 1, 1.0),
None => (stop_items.len() - 1, 1.0),
Some((end_index, end_stop)) => {
// `unwrap()` here should never fail because this is the end of
// a stop run, which is always bounded by a length or
Expand Down
53 changes: 31 additions & 22 deletions components/style/gecko/conversions.rs
Expand Up @@ -16,7 +16,8 @@ use gecko_bindings::structs::{nsStyleCoord_CalcValue, nsStyleImage};
use gecko_bindings::structs::{nsresult, SheetType};
use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordDataMut};
use stylesheets::{Origin, RulesMutateError};
use values::computed::{CalcLengthOrPercentage, Gradient, Image, LengthOrPercentage, LengthOrPercentageOrAuto};
use values::computed::{CalcLengthOrPercentage, Gradient, GradientItem, Image};
use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};

impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue {
Expand Down Expand Up @@ -160,7 +161,7 @@ impl nsStyleImage {
use values::computed::LengthOrPercentageOrKeyword;
use values::specified::{HorizontalDirection, SizeKeyword, VerticalDirection};

let stop_count = gradient.stops.len();
let stop_count = gradient.items.len();
if stop_count >= ::std::u32::MAX as usize {
warn!("stylo: Prevented overflow due to too many gradient stops");
return;
Expand Down Expand Up @@ -280,32 +281,40 @@ impl nsStyleImage {
},
};

for (index, stop) in gradient.stops.iter().enumerate() {
for (index, item) in gradient.items.iter().enumerate() {
// NB: stops are guaranteed to be none in the gecko side by
// default.
let mut coord: nsStyleCoord = nsStyleCoord::null();
coord.set(stop.position);
let color = match stop.color {
CSSColor::CurrentColor => {
// TODO(emilio): gecko just stores an nscolor,
// and it doesn't seem to support currentColor
// as value in a gradient.
//
// Double-check it and either remove
// currentColor for servo or see how gecko
// handles this.
0
},
CSSColor::RGBA(ref rgba) => convert_rgba_to_nscolor(rgba),
};

let mut stop = unsafe {
let mut gecko_stop = unsafe {
&mut (*gecko_gradient).mStops[index]
};
let mut coord = nsStyleCoord::null();

match *item {
GradientItem::ColorStop(ref stop) => {
gecko_stop.mColor = match stop.color {
CSSColor::CurrentColor => {
// TODO(emilio): gecko just stores an nscolor,
// and it doesn't seem to support currentColor
// as value in a gradient.
//
// Double-check it and either remove
// currentColor for servo or see how gecko
// handles this.
0
},
CSSColor::RGBA(ref rgba) => convert_rgba_to_nscolor(rgba),
};
gecko_stop.mIsInterpolationHint = false;
coord.set(stop.position);
},
GradientItem::InterpolationHint(hint) => {
gecko_stop.mIsInterpolationHint = true;
coord.set(Some(hint));
}
}

stop.mColor = color;
stop.mIsInterpolationHint = false;
stop.mLocation.move_from(coord);
gecko_stop.mLocation.move_from(coord);
}

unsafe {
Expand Down
65 changes: 56 additions & 9 deletions components/style/values/computed/image.rs
Expand Up @@ -120,7 +120,7 @@ impl ToCss for Image {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Gradient {
/// The color stops.
pub stops: Vec<ColorStop>,
pub items: Vec<GradientItem>,
/// True if this is a repeating gradient.
pub repeating: bool,
/// Gradient kind can be linear or radial.
Expand Down Expand Up @@ -149,9 +149,9 @@ impl ToCss for Gradient {
try!(position.to_css(dest));
},
}
for stop in &self.stops {
for item in &self.items {
try!(dest.write_str(", "));
try!(stop.to_css(dest));
try!(item.to_css(dest));
}
try!(dest.write_str(")"));
Ok(())
Expand All @@ -169,8 +169,8 @@ impl fmt::Debug for Gradient {
},
}

for stop in &self.stops {
let _ = write!(f, ", {:?}", stop);
for item in &self.items {
let _ = write!(f, ", {:?}", item);
}
Ok(())
}
Expand All @@ -182,13 +182,13 @@ impl ToComputedValue for specified::Gradient {
#[inline]
fn to_computed_value(&self, context: &Context) -> Gradient {
let specified::Gradient {
ref stops,
ref items,
repeating,
ref gradient_kind,
compat_mode,
} = *self;
Gradient {
stops: stops.iter().map(|s| s.to_computed_value(context)).collect(),
items: items.iter().map(|s| s.to_computed_value(context)).collect(),
repeating: repeating,
gradient_kind: gradient_kind.to_computed_value(context),
compat_mode: compat_mode,
Expand All @@ -198,13 +198,13 @@ impl ToComputedValue for specified::Gradient {
#[inline]
fn from_computed_value(computed: &Gradient) -> Self {
let Gradient {
ref stops,
ref items,
repeating,
ref gradient_kind,
compat_mode,
} = *computed;
specified::Gradient {
stops: stops.iter().map(ToComputedValue::from_computed_value).collect(),
items: items.iter().map(ToComputedValue::from_computed_value).collect(),
repeating: repeating,
gradient_kind: ToComputedValue::from_computed_value(gradient_kind),
compat_mode: compat_mode,
Expand Down Expand Up @@ -251,6 +251,53 @@ impl ToComputedValue for specified::GradientKind {
}
}

/// Specified values for color stops and interpolation hints.
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum GradientItem {
/// A color stop.
ColorStop(ColorStop),
/// An interpolation hint.
InterpolationHint(LengthOrPercentage),
}

impl ToCss for GradientItem {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
GradientItem::ColorStop(stop) => stop.to_css(dest),
GradientItem::InterpolationHint(hint) => hint.to_css(dest),
}
}
}

impl ToComputedValue for specified::GradientItem {
type ComputedValue = GradientItem;

#[inline]
fn to_computed_value(&self, context: &Context) -> GradientItem {
match *self {
specified::GradientItem::ColorStop(ref stop) => {
GradientItem::ColorStop(stop.to_computed_value(context))
},
specified::GradientItem::InterpolationHint(ref hint) => {
GradientItem::InterpolationHint(hint.to_computed_value(context))
},
}
}

#[inline]
fn from_computed_value(computed: &GradientItem) -> Self {
match *computed {
GradientItem::ColorStop(ref stop) => {
specified::GradientItem::ColorStop(ToComputedValue::from_computed_value(stop))
},
GradientItem::InterpolationHint(ref hint) => {
specified::GradientItem::InterpolationHint(ToComputedValue::from_computed_value(hint))
},
}
}
}

/// Computed values for one color stop in a linear gradient.
/// https://drafts.csswg.org/css-images/#typedef-color-stop-list
#[derive(Clone, PartialEq, Copy)]
Expand Down
4 changes: 2 additions & 2 deletions components/style/values/computed/mod.rs
Expand Up @@ -17,8 +17,8 @@ use super::specified::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as G

pub use app_units::Au;
pub use cssparser::Color as CSSColor;
pub use self::image::{AngleOrCorner, EndingShape as GradientShape, Gradient, GradientKind, Image, ImageRect};
pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword};
pub use self::image::{AngleOrCorner, EndingShape as GradientShape, Gradient, GradientItem};
pub use self::image::{GradientKind, Image, ImageRect, LengthOrKeyword, LengthOrPercentageOrKeyword};
pub use super::{Auto, Either, None_};
#[cfg(feature = "gecko")]
pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
Expand Down
63 changes: 48 additions & 15 deletions components/style/values/specified/image.rs
Expand Up @@ -92,8 +92,8 @@ impl Image {
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Gradient {
/// The color stops.
pub stops: Vec<ColorStop>,
/// The color stops and interpolation hints.
pub items: Vec<GradientItem>,
/// True if this is a repeating gradient.
pub repeating: bool,
/// Gradients can be linear or radial.
Expand Down Expand Up @@ -132,13 +132,13 @@ impl ToCss for Gradient {
}
},
}
for stop in &self.stops {
for item in &self.items {
if !skipcomma {
try!(dest.write_str(", "));
} else {
skipcomma = false;
}
try!(stop.to_css(dest));
try!(item.to_css(dest));
}
dest.write_str(")")
}
Expand All @@ -148,18 +148,18 @@ impl Gradient {
/// Parses a gradient from the given arguments.
pub fn parse_function(context: &ParserContext, input: &mut Parser) -> Result<Gradient, ()> {
fn parse<F>(context: &ParserContext, input: &mut Parser, parse_kind: F)
-> Result<(GradientKind, Vec<ColorStop>), ()>
-> Result<(GradientKind, Vec<GradientItem>), ()>
where F: FnOnce(&ParserContext, &mut Parser) -> Result<GradientKind, ()>
{
input.parse_nested_block(|input| {
let kind = try!(parse_kind(context, input));
let stops = try!(input.parse_comma_separated(|i| ColorStop::parse(context, i)));
Ok((kind, stops))
let items = try!(Gradient::parse_items(context, input));
Ok((kind, items))
})
};
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, items) = match_ignore_ascii_case! { &try!(input.expect_function()),
"linear-gradient" => {
try!(parse(context, input, GradientKind::parse_modern_linear))
},
Expand Down Expand Up @@ -195,18 +195,31 @@ impl Gradient {
_ => { return Err(()); }
};

// https://drafts.csswg.org/css-images/#typedef-color-stop-list
if stops.len() < 2 {
return Err(())
}

Ok(Gradient {
stops: stops,
items: items,
repeating: repeating,
gradient_kind: gradient_kind,
compat_mode: compat_mode,
})
}

fn parse_items(context: &ParserContext, input: &mut Parser) -> Result<Vec<GradientItem>, ()> {
let mut seen_stop = false;
let items = try!(input.parse_comma_separated(|input| {
if seen_stop {
if let Ok(hint) = input.try(|i| LengthOrPercentage::parse(context, i)) {
seen_stop = false;
return Ok(GradientItem::InterpolationHint(hint));
}
}
seen_stop = true;
ColorStop::parse(context, input).map(GradientItem::ColorStop)
}));
if !seen_stop || items.len() < 2 {
return Err(());
}
Ok(items)
}
}

/// Specified values for CSS linear or radial gradients.
Expand Down Expand Up @@ -489,7 +502,27 @@ impl AngleOrCorner {
}
}

/// Specified values for one color stop in a linear gradient.
/// Specified values for color stops and interpolation hints.
/// https://drafts.csswg.org/css-images-4/#color-stop-syntax
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum GradientItem {
/// A color stop.
ColorStop(ColorStop),
/// An interpolation hint.
InterpolationHint(LengthOrPercentage),
}

impl ToCss for GradientItem {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
GradientItem::ColorStop(ref stop) => stop.to_css(dest),
GradientItem::InterpolationHint(ref hint) => hint.to_css(dest),
}
}
}

/// Specified values for one color stop in a gradient.
/// https://drafts.csswg.org/css-images/#typedef-color-stop-list
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
Expand Down
4 changes: 2 additions & 2 deletions components/style/values/specified/mod.rs
Expand Up @@ -29,8 +29,8 @@ pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, Justify
pub use self::color::Color;
pub use self::grid::{GridLine, TrackKeyword};
pub use self::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient};
pub use self::image::{GradientKind, HorizontalDirection, Image, ImageRect, LengthOrKeyword};
pub use self::image::{LengthOrPercentageOrKeyword, SizeKeyword, VerticalDirection};
pub use self::image::{GradientItem, GradientKind, HorizontalDirection, Image, ImageRect};
pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword, SizeKeyword, VerticalDirection};
pub use self::length::AbsoluteLength;
pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage};
pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
Expand Down

0 comments on commit f598adc

Please sign in to comment.