mirror of
https://github.com/servo/servo.git
synced 2025-07-23 23:33:43 +01:00
Parse interpolation hints (fixes #15166)
This commit is contained in:
parent
8824a68063
commit
ae82cdab34
6 changed files with 152 additions and 57 deletions
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
stop.mColor = color;
|
||||
stop.mIsInterpolationHint = false;
|
||||
stop.mLocation.move_from(coord);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
gecko_stop.mLocation.move_from(coord);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
|
|
|
@ -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.
|
||||
|
@ -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(())
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)]
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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.
|
||||
|
@ -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(")")
|
||||
}
|
||||
|
@ -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))
|
||||
},
|
||||
|
@ -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.
|
||||
|
@ -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))]
|
||||
|
|
|
@ -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};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue