Auto merge of #21653 - emilio:gecko-sync, r=emilio

style: sync changes from mozilla-central.

See each individual commit for details.

<!-- 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/21653)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-09-09 10:22:38 -04:00 committed by GitHub
commit a39a57c076
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 255 additions and 43 deletions

View file

@ -379,7 +379,7 @@ ${helpers.predefined_type(
"OffsetPath",
"computed::OffsetPath::none()",
products="gecko",
animation_value_type="none",
animation_value_type="ComputedValue",
gecko_pref="layout.css.motion-path.enabled",
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
spec="https://drafts.fxtf.org/motion-1/#offset-path-property",

View file

@ -392,3 +392,14 @@ where
))
}
}
impl<T> ToAnimatedZero for Box<[T]>
where
T: ToAnimatedZero,
{
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
let v = self.iter().map(|v| v.to_animated_zero()).collect::<Result<Vec<_>, _>>()?;
Ok(v.into_boxed_slice())
}
}

View file

@ -54,7 +54,6 @@ pub enum ShapeSource<BasicShape, ReferenceBox, ImageOrUrl> {
Shape(BasicShape, Option<ReferenceBox>),
#[animation(error)]
Box(ReferenceBox),
#[animation(error)]
#[css(function)]
Path(Path),
#[animation(error)]
@ -152,10 +151,12 @@ pub enum FillRule {
///
/// https://drafts.csswg.org/css-shapes-2/#funcdef-path
#[css(comma)]
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
#[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
ToComputedValue, ToCss)]
pub struct Path {
/// The filling rule for the svg path.
#[css(skip_if = "fill_is_default")]
#[animation(constant)]
pub fill: FillRule,
/// The svg path data.
pub path: SVGPathData,
@ -177,6 +178,13 @@ where
{
this.compute_squared_distance(other)
},
(
&ShapeSource::Path(ref this),
&ShapeSource::Path(ref other),
) if this.fill == other.fill =>
{
this.path.compute_squared_distance(&other.path)
}
_ => Err(()),
}
}

View file

@ -12,7 +12,8 @@ use values::specified::SVGPathData;
/// The offset-path value.
///
/// https://drafts.fxtf.org/motion-1/#offset-path-property
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
SpecifiedValueInfo, ToAnimatedZero, ToComputedValue, ToCss)]
pub enum OffsetPath {
// We could merge SVGPathData into ShapeSource, so we could reuse them. However,
// we don't want to support other value for offset-path, so use SVGPathData only for now.
@ -20,6 +21,7 @@ pub enum OffsetPath {
#[css(function)]
Path(SVGPathData),
/// None value.
#[animation(error)]
None,
// Bug 1186329: Implement ray(), <basic-shape>, <geometry-box>, and <url>.
}

View file

@ -8,16 +8,20 @@ use cssparser::Parser;
use parser::{Parse, ParserContext};
use std::fmt::{self, Write};
use std::iter::{Cloned, Peekable};
use std::ops::AddAssign;
use std::slice;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
use style_traits::values::SequenceWriter;
use values::CSSFloat;
use values::animated::{Animate, Procedure};
use values::distance::{ComputeSquaredDistance, SquaredDistance};
/// The SVG path data.
///
/// https://www.w3.org/TR/SVG11/paths.html#PathData
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)]
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedZero,
ToComputedValue)]
pub struct SVGPathData(Box<[PathCommand]>);
impl SVGPathData {
@ -34,6 +38,17 @@ impl SVGPathData {
debug_assert!(!self.0.is_empty());
&self.0
}
/// Create a normalized copy of this path by converting each relative command to an absolute
/// command.
fn normalize(&self) -> Self {
let mut state = PathTraversalState {
subpath_start: CoordPair::new(0.0, 0.0),
pos: CoordPair::new(0.0, 0.0),
};
let result = self.0.iter().map(|seg| seg.normalize(&mut state)).collect::<Vec<_>>();
SVGPathData(result.into_boxed_slice())
}
}
impl ToCss for SVGPathData {
@ -82,6 +97,33 @@ impl Parse for SVGPathData {
}
}
impl Animate for SVGPathData {
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
if self.0.len() != other.0.len() {
return Err(());
}
let result = self.normalize().0
.iter()
.zip(other.normalize().0.iter())
.map(|(a, b)| a.animate(&b, procedure))
.collect::<Result<Vec<_>, _>>()?;
Ok(SVGPathData::new(result.into_boxed_slice()))
}
}
impl ComputeSquaredDistance for SVGPathData {
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
if self.0.len() != other.0.len() {
return Err(());
}
self.normalize().0
.iter()
.zip(other.normalize().0.iter())
.map(|(this, other)| this.compute_squared_distance(&other))
.sum()
}
}
/// The SVG path command.
/// The fields of these commands are self-explanatory, so we skip the documents.
@ -89,7 +131,8 @@ impl Parse for SVGPathData {
/// points of the Bézier curve in the spec.
///
/// https://www.w3.org/TR/SVG11/paths.html#PathData
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
SpecifiedValueInfo, ToAnimatedZero)]
#[allow(missing_docs)]
#[repr(C, u8)]
pub enum PathCommand {
@ -97,35 +140,131 @@ pub enum PathCommand {
/// https://www.w3.org/TR/SVG/paths.html#__svg__SVGPathSeg__PATHSEG_UNKNOWN
Unknown,
/// The "moveto" command.
MoveTo { point: CoordPair, absolute: bool },
MoveTo { point: CoordPair, absolute: IsAbsolute },
/// The "lineto" command.
LineTo { point: CoordPair, absolute: bool },
LineTo { point: CoordPair, absolute: IsAbsolute },
/// The horizontal "lineto" command.
HorizontalLineTo { x: CSSFloat, absolute: bool },
HorizontalLineTo { x: CSSFloat, absolute: IsAbsolute },
/// The vertical "lineto" command.
VerticalLineTo { y: CSSFloat, absolute: bool },
VerticalLineTo { y: CSSFloat, absolute: IsAbsolute },
/// The cubic Bézier curve command.
CurveTo { control1: CoordPair, control2: CoordPair, point: CoordPair, absolute: bool },
CurveTo { control1: CoordPair, control2: CoordPair, point: CoordPair, absolute: IsAbsolute },
/// The smooth curve command.
SmoothCurveTo { control2: CoordPair, point: CoordPair, absolute: bool },
SmoothCurveTo { control2: CoordPair, point: CoordPair, absolute: IsAbsolute },
/// The quadratic Bézier curve command.
QuadBezierCurveTo { control1: CoordPair, point: CoordPair, absolute: bool },
QuadBezierCurveTo { control1: CoordPair, point: CoordPair, absolute: IsAbsolute },
/// The smooth quadratic Bézier curve command.
SmoothQuadBezierCurveTo { point: CoordPair, absolute: bool },
SmoothQuadBezierCurveTo { point: CoordPair, absolute: IsAbsolute },
/// The elliptical arc curve command.
EllipticalArc {
rx: CSSFloat,
ry: CSSFloat,
angle: CSSFloat,
large_arc_flag: bool,
sweep_flag: bool,
#[animation(constant)]
large_arc_flag: ArcFlag,
#[animation(constant)]
sweep_flag: ArcFlag,
point: CoordPair,
absolute: bool
absolute: IsAbsolute
},
/// The "closepath" command.
ClosePath,
}
/// For internal SVGPath normalization.
#[allow(missing_docs)]
struct PathTraversalState {
subpath_start: CoordPair,
pos: CoordPair,
}
impl PathCommand {
/// Create a normalized copy of this PathCommand. Absolute commands will be copied as-is while
/// for relative commands an equivalent absolute command will be returned.
///
/// See discussion: https://github.com/w3c/svgwg/issues/321
fn normalize(&self, state: &mut PathTraversalState) -> Self {
use self::PathCommand::*;
match *self {
Unknown => Unknown,
ClosePath => {
state.pos = state.subpath_start;
ClosePath
},
MoveTo { mut point, absolute } => {
if !absolute.is_yes() {
point += state.pos;
}
state.pos = point;
state.subpath_start = point;
MoveTo { point, absolute: IsAbsolute::Yes }
},
LineTo { mut point, absolute } => {
if !absolute.is_yes() {
point += state.pos;
}
state.pos = point;
LineTo { point, absolute: IsAbsolute::Yes }
},
HorizontalLineTo { mut x, absolute } => {
if !absolute.is_yes() {
x += state.pos.0;
}
state.pos.0 = x;
HorizontalLineTo { x, absolute: IsAbsolute::Yes }
},
VerticalLineTo { mut y, absolute } => {
if !absolute.is_yes() {
y += state.pos.1;
}
state.pos.1 = y;
VerticalLineTo { y, absolute: IsAbsolute::Yes }
},
CurveTo { mut control1, mut control2, mut point, absolute } => {
if !absolute.is_yes() {
control1 += state.pos;
control2 += state.pos;
point += state.pos;
}
state.pos = point;
CurveTo { control1, control2, point, absolute: IsAbsolute::Yes }
},
SmoothCurveTo { mut control2, mut point, absolute } => {
if !absolute.is_yes() {
control2 += state.pos;
point += state.pos;
}
state.pos = point;
SmoothCurveTo { control2, point, absolute: IsAbsolute::Yes }
},
QuadBezierCurveTo { mut control1, mut point, absolute } => {
if !absolute.is_yes() {
control1 += state.pos;
point += state.pos;
}
state.pos = point;
QuadBezierCurveTo { control1, point, absolute: IsAbsolute::Yes }
},
SmoothQuadBezierCurveTo { mut point, absolute } => {
if !absolute.is_yes() {
point += state.pos;
}
state.pos = point;
SmoothQuadBezierCurveTo { point, absolute: IsAbsolute::Yes }
},
EllipticalArc { rx, ry, angle, large_arc_flag, sweep_flag, mut point, absolute } => {
if !absolute.is_yes() {
point += state.pos;
}
state.pos = point;
EllipticalArc {
rx, ry, angle, large_arc_flag, sweep_flag, point, absolute: IsAbsolute::Yes
}
},
}
}
}
impl ToCss for PathCommand {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
@ -136,17 +275,17 @@ impl ToCss for PathCommand {
Unknown => dest.write_char('X'),
ClosePath => dest.write_char('Z'),
MoveTo { point, absolute } => {
dest.write_char(if absolute { 'M' } else { 'm' })?;
dest.write_char(if absolute.is_yes() { 'M' } else { 'm' })?;
dest.write_char(' ')?;
point.to_css(dest)
}
LineTo { point, absolute } => {
dest.write_char(if absolute { 'L' } else { 'l' })?;
dest.write_char(if absolute.is_yes() { 'L' } else { 'l' })?;
dest.write_char(' ')?;
point.to_css(dest)
}
CurveTo { control1, control2, point, absolute } => {
dest.write_char(if absolute { 'C' } else { 'c' })?;
dest.write_char(if absolute.is_yes() { 'C' } else { 'c' })?;
dest.write_char(' ')?;
control1.to_css(dest)?;
dest.write_char(' ')?;
@ -155,14 +294,14 @@ impl ToCss for PathCommand {
point.to_css(dest)
},
QuadBezierCurveTo { control1, point, absolute } => {
dest.write_char(if absolute { 'Q' } else { 'q' })?;
dest.write_char(if absolute.is_yes() { 'Q' } else { 'q' })?;
dest.write_char(' ')?;
control1.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)
},
EllipticalArc { rx, ry, angle, large_arc_flag, sweep_flag, point, absolute } => {
dest.write_char(if absolute { 'A' } else { 'a' })?;
dest.write_char(if absolute.is_yes() { 'A' } else { 'a' })?;
dest.write_char(' ')?;
rx.to_css(dest)?;
dest.write_char(' ')?;
@ -170,31 +309,31 @@ impl ToCss for PathCommand {
dest.write_char(' ')?;
angle.to_css(dest)?;
dest.write_char(' ')?;
(large_arc_flag as i32).to_css(dest)?;
large_arc_flag.to_css(dest)?;
dest.write_char(' ')?;
(sweep_flag as i32).to_css(dest)?;
sweep_flag.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)
},
HorizontalLineTo { x, absolute } => {
dest.write_char(if absolute { 'H' } else { 'h' })?;
dest.write_char(if absolute.is_yes() { 'H' } else { 'h' })?;
dest.write_char(' ')?;
x.to_css(dest)
},
VerticalLineTo { y, absolute } => {
dest.write_char(if absolute { 'V' } else { 'v' })?;
dest.write_char(if absolute.is_yes() { 'V' } else { 'v' })?;
dest.write_char(' ')?;
y.to_css(dest)
},
SmoothCurveTo { control2, point, absolute } => {
dest.write_char(if absolute { 'S' } else { 's' })?;
dest.write_char(if absolute.is_yes() { 'S' } else { 's' })?;
dest.write_char(' ')?;
control2.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)
},
SmoothQuadBezierCurveTo { point, absolute } => {
dest.write_char(if absolute { 'T' } else { 't' })?;
dest.write_char(if absolute.is_yes() { 'T' } else { 't' })?;
dest.write_char(' ')?;
point.to_css(dest)
},
@ -202,9 +341,27 @@ impl ToCss for PathCommand {
}
}
/// The path command absolute type.
#[allow(missing_docs)]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
SpecifiedValueInfo, ToAnimatedZero)]
#[repr(u8)]
pub enum IsAbsolute {
Yes,
No,
}
impl IsAbsolute {
/// Return true if this is IsAbsolute::Yes.
#[inline]
pub fn is_yes(&self) -> bool {
*self == IsAbsolute::Yes
}
}
/// The path coord type.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
SpecifiedValueInfo, ToAnimatedZero, ToCss)]
#[repr(C)]
pub struct CoordPair(CSSFloat, CSSFloat);
@ -216,6 +373,36 @@ impl CoordPair {
}
}
impl AddAssign for CoordPair {
#[inline]
fn add_assign(&mut self, other: Self) {
self.0 += other.0;
self.1 += other.1;
}
}
/// The EllipticalArc flag type.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)]
#[repr(C)]
pub struct ArcFlag(bool);
impl ToCss for ArcFlag {
#[inline]
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write
{
(self.0 as i32).to_css(dest)
}
}
impl ComputeSquaredDistance for ArcFlag {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
(self.0 as i32).compute_squared_distance(&(other.0 as i32))
}
}
/// SVG Path parser.
struct PathParser<'a> {
@ -276,7 +463,11 @@ impl<'a> PathParser<'a> {
match self.chars.next() {
Some(command) => {
let abs = command.is_ascii_uppercase();
let abs = if command.is_ascii_uppercase() {
IsAbsolute::Yes
} else {
IsAbsolute::No
};
macro_rules! parse_command {
( $($($p:pat)|+ => $parse_func:ident,)* ) => {
match command {
@ -299,7 +490,7 @@ impl<'a> PathParser<'a> {
b'S' | b's' => parse_smooth_curveto,
b'Q' | b'q' => parse_quadratic_bezier_curveto,
b'T' | b't' => parse_smooth_quadratic_bezier_curveto,
b'A' | b'a' => parse_elliprical_arc,
b'A' | b'a' => parse_elliptical_arc,
);
},
_ => break, // no more commands.
@ -317,7 +508,7 @@ impl<'a> PathParser<'a> {
skip_wsp(&mut self.chars);
let point = parse_coord(&mut self.chars)?;
let absolute = command == b'M';
let absolute = if command == b'M' { IsAbsolute::Yes } else { IsAbsolute::No };
self.path.push(PathCommand::MoveTo { point, absolute } );
// End of string or the next character is a possible new command.
@ -333,58 +524,58 @@ impl<'a> PathParser<'a> {
}
/// Parse "closepath" command.
fn parse_closepath(&mut self, _absolute: bool) -> Result<(), ()> {
fn parse_closepath(&mut self, _absolute: IsAbsolute) -> Result<(), ()> {
self.path.push(PathCommand::ClosePath);
Ok(())
}
/// Parse "lineto" command.
fn parse_lineto(&mut self, absolute: bool) -> Result<(), ()> {
fn parse_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, LineTo, [ point => parse_coord ])
}
/// Parse horizontal "lineto" command.
fn parse_h_lineto(&mut self, absolute: bool) -> Result<(), ()> {
fn parse_h_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ])
}
/// Parse vertical "lineto" command.
fn parse_v_lineto(&mut self, absolute: bool) -> Result<(), ()> {
fn parse_v_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ])
}
/// Parse cubic Bézier curve command.
fn parse_curveto(&mut self, absolute: bool) -> Result<(), ()> {
fn parse_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, CurveTo, [
control1 => parse_coord, control2 => parse_coord, point => parse_coord
])
}
/// Parse smooth "curveto" command.
fn parse_smooth_curveto(&mut self, absolute: bool) -> Result<(), ()> {
fn parse_smooth_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, SmoothCurveTo, [
control2 => parse_coord, point => parse_coord
])
}
/// Parse quadratic Bézier curve command.
fn parse_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> {
fn parse_quadratic_bezier_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, QuadBezierCurveTo, [
control1 => parse_coord, point => parse_coord
])
}
/// Parse smooth quadratic Bézier curveto command.
fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> {
fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ])
}
/// Parse elliptical arc curve command.
fn parse_elliprical_arc(&mut self, absolute: bool) -> Result<(), ()> {
fn parse_elliptical_arc(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
// Parse a flag whose value is '0' or '1'; otherwise, return Err(()).
let parse_flag = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| -> Result<bool, ()> {
let parse_flag = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| {
match iter.next() {
Some(c) if c == b'0' || c == b'1' => Ok(c == b'1'),
Some(c) if c == b'0' || c == b'1' => Ok(ArcFlag(c == b'1')),
_ => Err(()),
}
};