diff options
Diffstat (limited to 'servo/components/style/values')
23 files changed, 1250 insertions, 690 deletions
diff --git a/servo/components/style/values/computed/basic_shape.rs b/servo/components/style/values/computed/basic_shape.rs index d39110ec1c..21df7baf93 100644 --- a/servo/components/style/values/computed/basic_shape.rs +++ b/servo/components/style/values/computed/basic_shape.rs @@ -7,9 +7,12 @@ //! //! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape +use crate::values::animated::{Animate, Procedure}; +use crate::values::computed::angle::Angle; use crate::values::computed::url::ComputedUrl; use crate::values::computed::{Image, LengthPercentage, NonNegativeLengthPercentage, Position}; use crate::values::generics::basic_shape as generic; +use crate::values::specified::svg_path::{CoordPair, PathCommand}; /// A computed alias for FillRule. pub use crate::values::generics::basic_shape::FillRule; @@ -21,8 +24,13 @@ pub type ClipPath = generic::GenericClipPath<BasicShape, ComputedUrl>; pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>; /// A computed basic shape. -pub type BasicShape = - generic::GenericBasicShape<Position, LengthPercentage, NonNegativeLengthPercentage, InsetRect>; +pub type BasicShape = generic::GenericBasicShape< + Angle, + Position, + LengthPercentage, + NonNegativeLengthPercentage, + InsetRect, +>; /// The computed value of `inset()`. pub type InsetRect = generic::GenericInsetRect<LengthPercentage, NonNegativeLengthPercentage>; @@ -35,3 +43,179 @@ pub type Ellipse = generic::Ellipse<Position, NonNegativeLengthPercentage>; /// The computed value of `ShapeRadius`. pub type ShapeRadius = generic::GenericShapeRadius<NonNegativeLengthPercentage>; + +/// The computed value of `shape()`. +pub type Shape = generic::Shape<Angle, LengthPercentage>; + +/// The computed value of `ShapeCommand`. +pub type ShapeCommand = generic::GenericShapeCommand<Angle, LengthPercentage>; + +/// The computed value of `PathOrShapeFunction`. +pub type PathOrShapeFunction = generic::GenericPathOrShapeFunction<Angle, LengthPercentage>; + +/// The computed value of `CoordinatePair`. +pub type CoordinatePair = generic::CoordinatePair<LengthPercentage>; + +/// Animate from `Shape` to `Path`, and vice versa. +macro_rules! animate_shape { + ( + $from:ident, + $to:ident, + $procedure:ident, + $from_as_shape:tt, + $to_as_shape:tt + ) => {{ + // Check fill-rule. + if $from.fill != $to.fill { + return Err(()); + } + + // Check the list of commands. (This is a specialized lists::by_computed_value::animate().) + let from_cmds = $from.commands(); + let to_cmds = $to.commands(); + if from_cmds.len() != to_cmds.len() { + return Err(()); + } + let commands = from_cmds + .iter() + .zip(to_cmds.iter()) + .map(|(from_cmd, to_cmd)| { + $from_as_shape(from_cmd).animate(&$to_as_shape(to_cmd), $procedure) + }) + .collect::<Result<Vec<ShapeCommand>, ()>>()?; + + Ok(Shape { + fill: $from.fill, + commands: commands.into(), + }) + }}; +} + +impl Animate for PathOrShapeFunction { + #[inline] + fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { + // Per spec, commands are "the same" if they use the same command keyword, and use the same + // <by-to> keyword. For curve and smooth, they also must have the same number of control + // points. Therefore, we don't have to do normalization here. (Note that we do + // normalization if we animate from path() to path(). See svg_path.rs for more details.) + // + // https://drafts.csswg.org/css-shapes-2/#interpolating-shape + match (self, other) { + (Self::Path(ref from), Self::Path(ref to)) => { + from.animate(to, procedure).map(Self::Path) + }, + (Self::Shape(ref from), Self::Shape(ref to)) => { + from.animate(to, procedure).map(Self::Shape) + }, + (Self::Shape(ref from), Self::Path(ref to)) => { + // Animate from shape() to path(). We convert each PathCommand into ShapeCommand, + // and return shape(). + animate_shape!( + from, + to, + procedure, + (|shape_cmd| shape_cmd), + (|path_cmd| ShapeCommand::from(path_cmd)) + ) + .map(Self::Shape) + }, + (Self::Path(ref from), Self::Shape(ref to)) => { + // Animate from path() to shape(). We convert each PathCommand into ShapeCommand, + // and return shape(). + animate_shape!( + from, + to, + procedure, + (|path_cmd| ShapeCommand::from(path_cmd)), + (|shape_cmd| shape_cmd) + ) + .map(Self::Shape) + }, + } + } +} + +impl From<&PathCommand> for ShapeCommand { + #[inline] + fn from(path: &PathCommand) -> Self { + use crate::values::computed::CSSPixelLength; + match path { + &PathCommand::Close => Self::Close, + &PathCommand::Move { by_to, ref point } => Self::Move { + by_to, + point: point.into(), + }, + &PathCommand::Line { by_to, ref point } => Self::Move { + by_to, + point: point.into(), + }, + &PathCommand::HLine { by_to, x } => Self::HLine { + by_to, + x: LengthPercentage::new_length(CSSPixelLength::new(x)), + }, + &PathCommand::VLine { by_to, y } => Self::VLine { + by_to, + y: LengthPercentage::new_length(CSSPixelLength::new(y)), + }, + &PathCommand::CubicCurve { + by_to, + ref point, + ref control1, + ref control2, + } => Self::CubicCurve { + by_to, + point: point.into(), + control1: control1.into(), + control2: control2.into(), + }, + &PathCommand::QuadCurve { + by_to, + ref point, + ref control1, + } => Self::QuadCurve { + by_to, + point: point.into(), + control1: control1.into(), + }, + &PathCommand::SmoothCubic { + by_to, + ref point, + ref control2, + } => Self::SmoothCubic { + by_to, + point: point.into(), + control2: control2.into(), + }, + &PathCommand::SmoothQuad { by_to, ref point } => Self::SmoothQuad { + by_to, + point: point.into(), + }, + &PathCommand::Arc { + by_to, + ref point, + ref radii, + arc_sweep, + arc_size, + rotate, + } => Self::Arc { + by_to, + point: point.into(), + radii: radii.into(), + arc_sweep, + arc_size, + rotate: Angle::from_degrees(rotate), + }, + } + } +} + +impl From<&CoordPair> for CoordinatePair { + #[inline] + fn from(p: &CoordPair) -> Self { + use crate::values::computed::CSSPixelLength; + Self::new( + LengthPercentage::new_length(CSSPixelLength::new(p.x)), + LengthPercentage::new_length(CSSPixelLength::new(p.y)), + ) + } +} diff --git a/servo/components/style/values/computed/box.rs b/servo/components/style/values/computed/box.rs index 62811d9851..0dea1c7ab9 100644 --- a/servo/components/style/values/computed/box.rs +++ b/servo/components/style/values/computed/box.rs @@ -383,6 +383,15 @@ impl Zoom { if self == Self::ONE { return value; } - self.value() * value + value * self.value() + } + + /// Returns the un-zoomed value. + #[inline] + pub fn unzoom(self, value: f32) -> f32 { + if self == Self::ONE { + return value; + } + value / self.value() } } diff --git a/servo/components/style/values/computed/font.rs b/servo/components/style/values/computed/font.rs index de0a5e372b..e492584f75 100644 --- a/servo/components/style/values/computed/font.rs +++ b/servo/components/style/values/computed/font.rs @@ -8,7 +8,7 @@ use crate::parser::{Parse, ParserContext}; use crate::values::animated::ToAnimatedValue; use crate::values::computed::{ Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, Number, Percentage, - ToComputedValue, + ToComputedValue, Zoom, }; use crate::values::generics::font::{ FeatureTagValue, FontSettings, TaggedFontValue, VariationValue, @@ -278,6 +278,16 @@ impl FontSize { self.used_size.0 } + /// Apply zoom to the font-size. This is usually done by ToComputedValue. + #[inline] + pub fn zoom(&self, zoom: Zoom) -> Self { + Self { + computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))), + used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))), + keyword_info: self.keyword_info, + } + } + #[inline] /// Get default value of font size. pub fn medium() -> Self { @@ -1359,7 +1369,7 @@ impl ToResolvedValue for LineHeight { context.style.get_font(), wm, Some(context.element_info.element), - )) + ).to_resolved_value(context)) } #[inline] diff --git a/servo/components/style/values/computed/length.rs b/servo/components/style/values/computed/length.rs index e75676a76d..881efed126 100644 --- a/servo/components/style/values/computed/length.rs +++ b/servo/components/style/values/computed/length.rs @@ -6,12 +6,13 @@ use super::{Context, Number, ToComputedValue}; use crate::values::animated::ToAnimatedValue; -use crate::values::computed::NonNegativeNumber; +use crate::values::computed::{NonNegativeNumber, Zoom}; use crate::values::generics::length as generics; use crate::values::generics::length::{ GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize, }; use crate::values::generics::NonNegative; +use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue}; use crate::values::specified::length::{AbsoluteLength, FontBaseSize, LineHeightBase}; use crate::values::{specified, CSSFloat}; use crate::Zero; @@ -227,12 +228,24 @@ impl Size { ToAnimatedValue, ToAnimatedZero, ToComputedValue, - ToResolvedValue, ToShmem, )] #[repr(C)] pub struct CSSPixelLength(CSSFloat); +impl ToResolvedValue for CSSPixelLength { + type ResolvedValue = Self; + + fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue { + Self(context.style.effective_zoom.unzoom(self.0)) + } + + #[inline] + fn from_resolved_value(value: Self::ResolvedValue) -> Self { + value + } +} + impl fmt::Debug for CSSPixelLength { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f)?; @@ -271,6 +284,12 @@ impl CSSPixelLength { self.0 } + /// Zooms a particular length. + #[inline] + pub fn zoom(self, zoom: Zoom) -> Self { + Self::new(zoom.zoom(self.px())) + } + /// Return the length with app_unit i32 type. #[inline] pub fn to_i32_au(self) -> i32 { diff --git a/servo/components/style/values/computed/length_percentage.rs b/servo/components/style/values/computed/length_percentage.rs index 0dbd2de76d..c448025dd1 100644 --- a/servo/components/style/values/computed/length_percentage.rs +++ b/servo/components/style/values/computed/length_percentage.rs @@ -30,6 +30,7 @@ use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZer use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; use crate::values::generics::calc::{CalcUnits, PositivePercentageBasis}; use crate::values::generics::{calc, NonNegative}; +use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue}; use crate::values::specified::length::{FontBaseSize, LineHeightBase}; use crate::values::{specified, CSSFloat}; use crate::{Zero, ZeroNoPercent}; @@ -164,6 +165,25 @@ impl MallocSizeOf for LengthPercentage { } } +impl ToResolvedValue for LengthPercentage { + type ResolvedValue = Self; + + fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue { + if context.style.effective_zoom.is_one() { + return self; + } + match self.unpack() { + Unpacked::Length(l) => Self::new_length(l.to_resolved_value(context)), + Unpacked::Percentage(..) | Unpacked::Calc(..) => self, + } + } + + #[inline] + fn from_resolved_value(value: Self::ResolvedValue) -> Self { + value + } +} + /// An unpacked `<length-percentage>` that borrows the `calc()` variant. #[derive(Clone, Debug, PartialEq, ToCss)] enum Unpacked<'a> { diff --git a/servo/components/style/values/computed/ratio.rs b/servo/components/style/values/computed/ratio.rs index ae8997cfc0..6964eec3e4 100644 --- a/servo/components/style/values/computed/ratio.rs +++ b/servo/components/style/values/computed/ratio.rs @@ -9,7 +9,7 @@ use crate::values::computed::NonNegativeNumber; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; use crate::values::generics::ratio::Ratio as GenericRatio; use crate::{One, Zero}; -use std::cmp::{Ordering, PartialOrd}; +use std::cmp::Ordering; /// A computed <ratio> value. pub type Ratio = GenericRatio<NonNegativeNumber>; diff --git a/servo/components/style/values/generics/basic_shape.rs b/servo/components/style/values/generics/basic_shape.rs index 13d27995c1..ca7646fb13 100644 --- a/servo/components/style/values/generics/basic_shape.rs +++ b/servo/components/style/values/generics/basic_shape.rs @@ -10,7 +10,7 @@ use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; use crate::values::generics::border::GenericBorderRadius; use crate::values::generics::position::GenericPositionOrAuto; use crate::values::generics::rect::Rect; -use crate::values::specified::SVGPathData; +use crate::values::specified::svg_path::{PathCommand, SVGPathData}; use crate::Zero; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; @@ -181,8 +181,13 @@ pub use self::GenericShapeOutside as ShapeOutside; ToShmem, )] #[repr(C, u8)] -pub enum GenericBasicShape<Position, LengthPercentage, NonNegativeLengthPercentage, BasicShapeRect> -{ +pub enum GenericBasicShape< + Angle, + Position, + LengthPercentage, + NonNegativeLengthPercentage, + BasicShapeRect, +> { /// The <basic-shape-rect>. Rect(BasicShapeRect), /// Defines a circle with a center and a radius. @@ -199,10 +204,11 @@ pub enum GenericBasicShape<Position, LengthPercentage, NonNegativeLengthPercenta ), /// Defines a polygon with pair arguments. Polygon(GenericPolygon<LengthPercentage>), - /// Defines a path with SVG path syntax. - Path(Path), - // TODO: Bug 1823463. Add shape(). - // https://drafts.csswg.org/css-shapes-2/#shape-function + /// Defines a path() or shape(). + PathOrShape( + #[animation(field_bound)] + #[css(field_bound)] + GenericPathOrShapeFunction<Angle, LengthPercentage>), } pub use self::GenericBasicShape as BasicShape; @@ -366,6 +372,30 @@ pub use self::GenericPolygon as Polygon; #[repr(C)] pub struct PolygonCoord<LengthPercentage>(pub LengthPercentage, pub LengthPercentage); +/// path() function or shape() function. +#[derive( + Clone, + ComputeSquaredDistance, + Debug, + Deserialize, + MallocSizeOf, + PartialEq, + Serialize, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C, u8)] +pub enum GenericPathOrShapeFunction<Angle, LengthPercentage> { + /// Defines a path with SVG path syntax. + Path(Path), + /// Defines a shape function, which is identical to path() but it uses the CSS syntax. + Shape(#[css(field_bound)] Shape<Angle, LengthPercentage>), +} + // https://drafts.csswg.org/css-shapes/#typedef-fill-rule // NOTE: Basic shapes spec says that these are the only two values, however // https://www.w3.org/TR/SVG/painting.html#FillRuleProperty @@ -397,9 +427,9 @@ pub enum FillRule { Evenodd, } -/// The path function defined in css-shape-2. +/// The path function. /// -/// https://drafts.csswg.org/css-shapes-2/#funcdef-path +/// https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-path #[derive( Animate, Clone, @@ -426,6 +456,14 @@ pub struct Path { pub path: SVGPathData, } +impl Path { + /// Returns the slice of PathCommand. + #[inline] + pub fn commands(&self) -> &[PathCommand] { + self.path.commands() + } +} + impl<B, U> ToAnimatedZero for ClipPath<B, U> { fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) @@ -565,3 +603,479 @@ impl Default for FillRule { fn is_default<T: Default + PartialEq>(fill: &T) -> bool { *fill == Default::default() } + +/// The shape function defined in css-shape-2. +/// shape() = shape(<fill-rule>? from <coordinate-pair>, <shape-command>#) +/// +/// https://drafts.csswg.org/css-shapes-2/#shape-function +#[derive( + Clone, + Debug, + Deserialize, + MallocSizeOf, + PartialEq, + Serialize, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +pub struct Shape<Angle, LengthPercentage> { + /// The filling rule for this shape. + pub fill: FillRule, + /// The shape command data. Note that the starting point will be the first command in this + /// slice. + // Note: The first command is always GenericShapeCommand::Move. + pub commands: crate::OwnedSlice<GenericShapeCommand<Angle, LengthPercentage>>, +} + +impl<Angle, LengthPercentage> Shape<Angle, LengthPercentage> { + /// Returns the slice of GenericShapeCommand<..>. + #[inline] + pub fn commands(&self) -> &[GenericShapeCommand<Angle, LengthPercentage>] { + &self.commands + } +} + +impl<Angle, LengthPercentage> Animate for Shape<Angle, LengthPercentage> +where + Angle: Animate, + LengthPercentage: Animate, +{ + fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { + if self.fill != other.fill { + return Err(()); + } + let commands = + lists::by_computed_value::animate(&self.commands, &other.commands, procedure)?; + Ok(Self { + fill: self.fill, + commands, + }) + } +} + +impl<Angle, LengthPercentage> ComputeSquaredDistance for Shape<Angle, LengthPercentage> +where + Angle: ComputeSquaredDistance, + LengthPercentage: ComputeSquaredDistance, +{ + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + if self.fill != other.fill { + return Err(()); + } + lists::by_computed_value::squared_distance(&self.commands, &other.commands) + } +} + +impl<Angle, LengthPercentage> ToCss for Shape<Angle, LengthPercentage> +where + Angle: ToCss + Zero, + LengthPercentage: PartialEq + ToCss, +{ + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + use style_traits::values::SequenceWriter; + + // Per spec, we must have the first move command and at least one following command. + debug_assert!(self.commands.len() > 1); + + dest.write_str("shape(")?; + if !is_default(&self.fill) { + self.fill.to_css(dest)?; + dest.write_char(' ')?; + } + dest.write_str("from ")?; + match self.commands[0] { + ShapeCommand::Move { + by_to: _, + ref point, + } => point.to_css(dest)?, + _ => unreachable!("The first command must be move"), + } + dest.write_str(", ")?; + { + let mut writer = SequenceWriter::new(dest, ", "); + for command in self.commands.iter().skip(1) { + writer.item(command)?; + } + } + dest.write_char(')') + } +} + +/// This is a more general shape(path) command type, for both shape() and path(). +/// +/// https://www.w3.org/TR/SVG11/paths.html#PathData +/// https://drafts.csswg.org/css-shapes-2/#shape-function +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + Deserialize, + MallocSizeOf, + PartialEq, + Serialize, + SpecifiedValueInfo, + ToAnimatedValue, + ToAnimatedZero, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[allow(missing_docs)] +#[repr(C, u8)] +pub enum GenericShapeCommand<Angle, LengthPercentage> { + /// The move command. + Move { + by_to: ByTo, + point: CoordinatePair<LengthPercentage>, + }, + /// The line command. + Line { + by_to: ByTo, + point: CoordinatePair<LengthPercentage>, + }, + /// The hline command. + HLine { by_to: ByTo, x: LengthPercentage }, + /// The vline command. + VLine { by_to: ByTo, y: LengthPercentage }, + /// The cubic Bézier curve command. + CubicCurve { + by_to: ByTo, + point: CoordinatePair<LengthPercentage>, + control1: CoordinatePair<LengthPercentage>, + control2: CoordinatePair<LengthPercentage>, + }, + /// The quadratic Bézier curve command. + QuadCurve { + by_to: ByTo, + point: CoordinatePair<LengthPercentage>, + control1: CoordinatePair<LengthPercentage>, + }, + /// The smooth command. + SmoothCubic { + by_to: ByTo, + point: CoordinatePair<LengthPercentage>, + control2: CoordinatePair<LengthPercentage>, + }, + /// The smooth quadratic Bézier curve command. + SmoothQuad { + by_to: ByTo, + point: CoordinatePair<LengthPercentage>, + }, + /// The arc command. + Arc { + by_to: ByTo, + point: CoordinatePair<LengthPercentage>, + radii: CoordinatePair<LengthPercentage>, + arc_sweep: ArcSweep, + arc_size: ArcSize, + rotate: Angle, + }, + /// The closepath command. + Close, +} + +pub use self::GenericShapeCommand as ShapeCommand; + +impl<Angle, LengthPercentage> ToCss for ShapeCommand<Angle, LengthPercentage> +where + Angle: ToCss + Zero, + LengthPercentage: PartialEq + ToCss, +{ + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: fmt::Write, + { + use self::ShapeCommand::*; + match *self { + Move { by_to, ref point } => { + dest.write_str("move ")?; + by_to.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + Line { by_to, ref point } => { + dest.write_str("line ")?; + by_to.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + HLine { by_to, ref x } => { + dest.write_str("hline ")?; + by_to.to_css(dest)?; + dest.write_char(' ')?; + x.to_css(dest) + }, + VLine { by_to, ref y } => { + dest.write_str("vline ")?; + by_to.to_css(dest)?; + dest.write_char(' ')?; + y.to_css(dest) + }, + CubicCurve { + by_to, + ref point, + ref control1, + ref control2, + } => { + dest.write_str("curve ")?; + by_to.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest)?; + dest.write_str(" via ")?; + control1.to_css(dest)?; + dest.write_char(' ')?; + control2.to_css(dest) + }, + QuadCurve { + by_to, + ref point, + ref control1, + } => { + dest.write_str("curve ")?; + by_to.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest)?; + dest.write_str(" via ")?; + control1.to_css(dest) + }, + SmoothCubic { + by_to, + ref point, + ref control2, + } => { + dest.write_str("smooth ")?; + by_to.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest)?; + dest.write_str(" via ")?; + control2.to_css(dest) + }, + SmoothQuad { by_to, ref point } => { + dest.write_str("smooth ")?; + by_to.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + Arc { + by_to, + ref point, + ref radii, + arc_sweep, + arc_size, + ref rotate, + } => { + dest.write_str("arc ")?; + by_to.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest)?; + dest.write_str(" of ")?; + radii.x.to_css(dest)?; + if radii.x != radii.y { + dest.write_char(' ')?; + radii.y.to_css(dest)?; + } + + if matches!(arc_sweep, ArcSweep::Cw) { + dest.write_str(" cw")?; + } + + if matches!(arc_size, ArcSize::Large) { + dest.write_str(" large")?; + } + + if !rotate.is_zero() { + dest.write_str(" rotate ")?; + rotate.to_css(dest)?; + } + Ok(()) + }, + Close => dest.write_str("close"), + } + } +} + +/// This indicates the command is absolute or relative. +/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-by-to +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + Deserialize, + MallocSizeOf, + Parse, + PartialEq, + Serialize, + SpecifiedValueInfo, + ToAnimatedValue, + ToAnimatedZero, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(u8)] +pub enum ByTo { + /// This indicates that the <coordinate-pair>s are relative to the command’s starting point. + By, + /// This relative to the top-left corner of the reference box. + To, +} + +impl ByTo { + /// Return true if it is absolute, i.e. it is To. + #[inline] + pub fn is_abs(&self) -> bool { + matches!(self, ByTo::To) + } + + /// Create ByTo based on the flag if it is absolute. + #[inline] + pub fn new(is_abs: bool) -> Self { + if is_abs { + Self::To + } else { + Self::By + } + } +} + +/// Defines a pair of coordinates, representing a rightward and downward offset, respectively, from +/// a specified reference point. Percentages are resolved against the width or height, +/// respectively, of the reference box. +/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-coordinate-pair +#[allow(missing_docs)] +#[derive( + AddAssign, + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + Deserialize, + MallocSizeOf, + PartialEq, + Serialize, + SpecifiedValueInfo, + ToAnimatedValue, + ToAnimatedZero, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +pub struct CoordinatePair<LengthPercentage> { + pub x: LengthPercentage, + pub y: LengthPercentage, +} + +impl<LengthPercentage> CoordinatePair<LengthPercentage> { + /// Create a CoordinatePair. + #[inline] + pub fn new(x: LengthPercentage, y: LengthPercentage) -> Self { + Self { x, y } + } +} + +/// This indicates that the arc that is traced around the ellipse clockwise or counter-clockwise +/// from the center. +/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-arc-sweep +#[derive( + Clone, + Copy, + Debug, + Deserialize, + FromPrimitive, + MallocSizeOf, + Parse, + PartialEq, + Serialize, + SpecifiedValueInfo, + ToAnimatedValue, + ToAnimatedZero, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(u8)] +pub enum ArcSweep { + /// Counter-clockwise. The default value. (This also represents 0 in the svg path.) + Ccw = 0, + /// Clockwise. (This also represents 1 in the svg path.) + Cw = 1, +} + +impl Animate for ArcSweep { + fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { + use num_traits::FromPrimitive; + // If an arc command has different <arc-sweep> between its starting and ending list, then + // the interpolated result uses cw for any progress value between 0 and 1. + (*self as i32) + .animate(&(*other as i32), procedure) + .map(|v| ArcSweep::from_u8((v > 0) as u8).unwrap_or(ArcSweep::Ccw)) + } +} + +impl ComputeSquaredDistance for ArcSweep { + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + (*self as i32).compute_squared_distance(&(*other as i32)) + } +} + +/// This indicates that the larger or smaller, respectively, of the two possible arcs must be +/// chosen. +/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-arc-size +#[derive( + Clone, + Copy, + Debug, + Deserialize, + FromPrimitive, + MallocSizeOf, + Parse, + PartialEq, + Serialize, + SpecifiedValueInfo, + ToAnimatedValue, + ToAnimatedZero, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(u8)] +pub enum ArcSize { + /// Choose the small one. The default value. (This also represents 0 in the svg path.) + Small = 0, + /// Choose the large one. (This also represents 1 in the svg path.) + Large = 1, +} + +impl Animate for ArcSize { + fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { + use num_traits::FromPrimitive; + // If it has different <arc-size> keywords, then the interpolated result uses large for any + // progress value between 0 and 1. + (*self as i32) + .animate(&(*other as i32), procedure) + .map(|v| ArcSize::from_u8((v > 0) as u8).unwrap_or(ArcSize::Small)) + } +} + +impl ComputeSquaredDistance for ArcSize { + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + (*self as i32).compute_squared_distance(&(*other as i32)) + } +} diff --git a/servo/components/style/values/generics/counters.rs b/servo/components/style/values/generics/counters.rs index 1d4518c57b..3f23c74b33 100644 --- a/servo/components/style/values/generics/counters.rs +++ b/servo/components/style/values/generics/counters.rs @@ -4,11 +4,10 @@ //! Generic types for counters-related CSS values. -#[cfg(feature = "servo-layout-2013")] +#[cfg(feature = "servo")] use crate::computed_values::list_style_type::T as ListStyleType; #[cfg(feature = "gecko")] use crate::values::generics::CounterStyle; -#[cfg(any(feature = "gecko", feature = "servo-layout-2020"))] use crate::values::specified::Attr; use crate::values::CustomIdent; use std::fmt::{self, Write}; @@ -186,13 +185,13 @@ pub struct GenericCounters<I>( ); pub use self::GenericCounters as Counters; -#[cfg(feature = "servo-layout-2013")] +#[cfg(feature = "servo")] type CounterStyleType = ListStyleType; #[cfg(feature = "gecko")] type CounterStyleType = CounterStyle; -#[cfg(feature = "servo-layout-2013")] +#[cfg(feature = "servo")] #[inline] fn is_decimal(counter_type: &CounterStyleType) -> bool { *counter_type == ListStyleType::Decimal @@ -254,11 +253,9 @@ pub enum GenericContentItem<I> { /// Literal string content. String(crate::OwnedStr), /// `counter(name, style)`. - #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] #[css(comma, function)] Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyleType), /// `counters(name, separator, style)`. - #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] #[css(comma, function)] Counters( CustomIdent, @@ -266,16 +263,12 @@ pub enum GenericContentItem<I> { #[css(skip_if = "is_decimal")] CounterStyleType, ), /// `open-quote`. - #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] OpenQuote, /// `close-quote`. - #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] CloseQuote, /// `no-open-quote`. - #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] NoOpenQuote, /// `no-close-quote`. - #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] NoCloseQuote, /// `-moz-alt-content`. #[cfg(feature = "gecko")] @@ -286,7 +279,6 @@ pub enum GenericContentItem<I> { #[cfg(feature = "gecko")] MozLabelContent, /// `attr([namespace? `|`]? ident)` - #[cfg(any(feature = "gecko", feature = "servo-layout-2020"))] Attr(Attr), /// image-set(url) | url(url) Image(I), diff --git a/servo/components/style/values/generics/image.rs b/servo/components/style/values/generics/image.rs index 6fc0870e15..ca1c716052 100644 --- a/servo/components/style/values/generics/image.rs +++ b/servo/components/style/values/generics/image.rs @@ -41,7 +41,7 @@ pub enum GenericImage<G, ImageUrl, Color, Percentage, Resolution> { /// A paint worklet image. /// <https://drafts.css-houdini.org/css-paint-api/> - #[cfg(feature = "servo-layout-2013")] + #[cfg(feature = "servo")] PaintWorklet(PaintWorklet), /// A `<cross-fade()>` image. Storing this directly inside of @@ -416,7 +416,7 @@ where Image::None => dest.write_str("none"), Image::Url(ref url) => url.to_css(dest), Image::Gradient(ref gradient) => gradient.to_css(dest), - #[cfg(feature = "servo-layout-2013")] + #[cfg(feature = "servo")] Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest), #[cfg(feature = "gecko")] Image::Element(ref selector) => { diff --git a/servo/components/style/values/generics/transform.rs b/servo/components/style/values/generics/transform.rs index 3a65c460a7..4d70e4465c 100644 --- a/servo/components/style/values/generics/transform.rs +++ b/servo/components/style/values/generics/transform.rs @@ -11,7 +11,6 @@ use crate::values::specified::length::Length as SpecifiedLength; use crate::values::specified::length::LengthPercentage as SpecifiedLengthPercentage; use crate::values::{computed, CSSFloat}; use crate::{Zero, ZeroNoPercent}; -use euclid; use euclid::default::{Rect, Transform3D}; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; diff --git a/servo/components/style/values/resolved/mod.rs b/servo/components/style/values/resolved/mod.rs index 675f3cca68..d830474fe6 100644 --- a/servo/components/style/values/resolved/mod.rs +++ b/servo/components/style/values/resolved/mod.rs @@ -95,7 +95,6 @@ trivial_to_resolved_value!(computed::url::ComputedImageUrl); trivial_to_resolved_value!(crate::Namespace); #[cfg(feature = "servo")] trivial_to_resolved_value!(crate::Prefix); -trivial_to_resolved_value!(computed::LengthPercentage); trivial_to_resolved_value!(style_traits::values::specified::AllowedNumericType); trivial_to_resolved_value!(computed::TimingFunction); diff --git a/servo/components/style/values/specified/animation.rs b/servo/components/style/values/specified/animation.rs index 5a1f5003f3..552521711c 100644 --- a/servo/components/style/values/specified/animation.rs +++ b/servo/components/style/values/specified/animation.rs @@ -165,6 +165,12 @@ impl AnimationIterationCount { pub fn one() -> Self { Self::Number(NonNegativeNumber::new(1.0)) } + + /// Returns true if it's `1.0`. + #[inline] + pub fn is_one(&self) -> bool { + *self == Self::one() + } } /// A value for the `animation-name` property. @@ -230,6 +236,17 @@ pub enum AnimationDirection { AlternateReverse, } +impl AnimationDirection { + /// Returns true if the name matches any animation-direction keyword. + #[inline] + pub fn match_keywords(name: &AnimationName) -> bool { + if let Some(name) = name.as_atom() { + return name.with_str(|n| Self::from_ident(n).is_ok()); + } + false + } +} + /// https://drafts.csswg.org/css-animations/#animation-play-state #[derive(Copy, Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)] #[repr(u8)] @@ -239,6 +256,17 @@ pub enum AnimationPlayState { Paused, } +impl AnimationPlayState { + /// Returns true if the name matches any animation-play-state keyword. + #[inline] + pub fn match_keywords(name: &AnimationName) -> bool { + if let Some(name) = name.as_atom() { + return name.with_str(|n| Self::from_ident(n).is_ok()); + } + false + } +} + /// https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode #[derive(Copy, Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)] #[repr(u8)] @@ -250,6 +278,18 @@ pub enum AnimationFillMode { Both, } +impl AnimationFillMode { + /// Returns true if the name matches any animation-fill-mode keyword. + /// Note: animation-name:none is its initial value, so we don't have to match none here. + #[inline] + pub fn match_keywords(name: &AnimationName) -> bool { + if let Some(atom) = name.as_atom() { + return !name.is_none() && atom.with_str(|n| Self::from_ident(n).is_ok()); + } + false + } +} + /// https://drafts.csswg.org/css-animations-2/#animation-composition #[derive(Copy, Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)] #[repr(u8)] diff --git a/servo/components/style/values/specified/basic_shape.rs b/servo/components/style/values/specified/basic_shape.rs index 526296b735..8ed32c40c5 100644 --- a/servo/components/style/values/specified/basic_shape.rs +++ b/servo/components/style/values/specified/basic_shape.rs @@ -14,6 +14,7 @@ use crate::values::generics::basic_shape as generic; use crate::values::generics::basic_shape::{Path, PolygonCoord}; use crate::values::generics::position::{GenericPosition, GenericPositionOrAuto}; use crate::values::generics::rect::Rect; +use crate::values::specified::angle::Angle; use crate::values::specified::border::BorderRadius; use crate::values::specified::image::Image; use crate::values::specified::length::LengthPercentageOrAuto; @@ -40,6 +41,7 @@ pub type ShapePosition = GenericPosition<LengthPercentage, LengthPercentage>; /// A specified basic shape. pub type BasicShape = generic::GenericBasicShape< + Angle, ShapePosition, LengthPercentage, NonNegativeLengthPercentage, @@ -61,6 +63,12 @@ pub type ShapeRadius = generic::ShapeRadius<NonNegativeLengthPercentage>; /// The specified value of `Polygon`. pub type Polygon = generic::GenericPolygon<LengthPercentage>; +/// The specified value of `PathOrShapeFunction`. +pub type PathOrShapeFunction = generic::GenericPathOrShapeFunction<Angle, LengthPercentage>; + +/// The specified value of `ShapeCommand`. +pub type ShapeCommand = generic::GenericShapeCommand<Angle, LengthPercentage>; + /// The specified value of `xywh()`. /// Defines a rectangle via offsets from the top and left edge of the reference box, and a /// specified width and height. @@ -168,8 +176,8 @@ bitflags! { const POLYGON = 1 << 5; /// path(). const PATH = 1 << 6; - // TODO: Bug 1823463. Add shape(). - // const SHAPE = 1 << 7; + /// shape(). + const SHAPE = 1 << 7; /// All flags. const ALL = @@ -179,7 +187,8 @@ bitflags! { Self::CIRCLE.bits() | Self::ELLIPSE.bits() | Self::POLYGON.bits() | - Self::PATH.bits(); + Self::PATH.bits() | + Self::SHAPE.bits(); /// For shape-outside. const SHAPE_OUTSIDE = @@ -329,7 +338,17 @@ impl BasicShape { .map(BasicShape::Polygon) }, "path" if flags.contains(AllowedBasicShapes::PATH) => { - Path::parse_function_arguments(i, shape_type).map(BasicShape::Path) + Path::parse_function_arguments(i, shape_type) + .map(PathOrShapeFunction::Path) + .map(BasicShape::PathOrShape) + }, + "shape" + if flags.contains(AllowedBasicShapes::SHAPE) + && static_prefs::pref!("layout.css.basic-shape-shape.enabled") => + { + generic::Shape::parse_function_arguments(context, i, shape_type) + .map(PathOrShapeFunction::Shape) + .map(BasicShape::PathOrShape) }, _ => Err(location .new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone()))), @@ -490,7 +509,11 @@ impl Ellipse { } } -fn parse_fill_rule<'i, 't>(input: &mut Parser<'i, 't>, shape_type: ShapeType) -> FillRule { +fn parse_fill_rule<'i, 't>( + input: &mut Parser<'i, 't>, + shape_type: ShapeType, + expect_comma: bool, +) -> FillRule { match shape_type { // Per [1] and [2], we ignore `<fill-rule>` for outline shapes, so always use a default // value. @@ -508,7 +531,9 @@ fn parse_fill_rule<'i, 't>(input: &mut Parser<'i, 't>, shape_type: ShapeType) -> ShapeType::Filled => input .try_parse(|i| -> Result<_, ParseError> { let fill = FillRule::parse(i)?; - i.expect_comma()?; + if expect_comma { + i.expect_comma()?; + } Ok(fill) }) .unwrap_or_default(), @@ -532,7 +557,7 @@ impl Polygon { input: &mut Parser<'i, 't>, shape_type: ShapeType, ) -> Result<Self, ParseError<'i>> { - let fill = parse_fill_rule(input, shape_type); + let fill = parse_fill_rule(input, shape_type, true /* has comma */); let coordinates = input .parse_comma_separated(|i| { Ok(PolygonCoord( @@ -554,7 +579,7 @@ impl Path { ) -> Result<Self, ParseError<'i>> { use crate::values::specified::svg_path::AllowEmpty; - let fill = parse_fill_rule(input, shape_type); + let fill = parse_fill_rule(input, shape_type, true /* has comma */); let path = SVGPathData::parse(input, AllowEmpty::No)?; Ok(Path { fill, path }) } @@ -717,3 +742,168 @@ impl ToComputedValue for BasicShapeRect { Self::Inset(ToComputedValue::from_computed_value(computed)) } } + +impl generic::Shape<Angle, LengthPercentage> { + /// Parse the inner arguments of a `shape` function. + /// shape() = shape(<fill-rule>? from <coordinate-pair>, <shape-command>#) + fn parse_function_arguments<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + shape_type: ShapeType, + ) -> Result<Self, ParseError<'i>> { + let fill = parse_fill_rule(input, shape_type, false /* no following comma */); + + let mut first = true; + let commands = input.parse_comma_separated(|i| { + if first { + first = false; + + // The starting point for the first shape-command. It adds an initial absolute + // moveto to the list of path data commands, with the <coordinate-pair> measured + // from the top-left corner of the reference + i.expect_ident_matching("from")?; + Ok(ShapeCommand::Move { + by_to: generic::ByTo::To, + point: generic::CoordinatePair::parse(context, i)?, + }) + } else { + // The further path data commands. + ShapeCommand::parse(context, i) + } + })?; + + // We must have one starting point and at least one following <shape-command>. + if commands.len() < 2 { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + Ok(Self { + fill, + commands: commands.into(), + }) + } +} + +impl Parse for ShapeCommand { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result<Self, ParseError<'i>> { + use crate::values::generics::basic_shape::{ArcSize, ArcSweep, ByTo, CoordinatePair}; + + // <shape-command> = <move-command> | <line-command> | <hv-line-command> | + // <curve-command> | <smooth-command> | <arc-command> | close + Ok(try_match_ident_ignore_ascii_case! { input, + "close" => Self::Close, + "move" => { + let by_to = ByTo::parse(input)?; + let point = CoordinatePair::parse(context, input)?; + Self::Move { by_to, point } + }, + "line" => { + let by_to = ByTo::parse(input)?; + let point = CoordinatePair::parse(context, input)?; + Self::Line { by_to, point } + }, + "hline" => { + let by_to = ByTo::parse(input)?; + let x = LengthPercentage::parse(context, input)?; + Self::HLine { by_to, x } + }, + "vline" => { + let by_to = ByTo::parse(input)?; + let y = LengthPercentage::parse(context, input)?; + Self::VLine { by_to, y } + }, + "curve" => { + let by_to = ByTo::parse(input)?; + let point = CoordinatePair::parse(context, input)?; + input.expect_ident_matching("via")?; + let control1 = CoordinatePair::parse(context, input)?; + match input.try_parse(|i| CoordinatePair::parse(context, i)) { + Ok(control2) => Self::CubicCurve { + by_to, + point, + control1, + control2, + }, + Err(_) => Self::QuadCurve { + by_to, + point, + control1, + }, + } + }, + "smooth" => { + let by_to = ByTo::parse(input)?; + let point = CoordinatePair::parse(context, input)?; + if input.try_parse(|i| i.expect_ident_matching("via")).is_ok() { + let control2 = CoordinatePair::parse(context, input)?; + Self::SmoothCubic { + by_to, + point, + control2, + } + } else { + Self::SmoothQuad { by_to, point } + } + }, + "arc" => { + let by_to = ByTo::parse(input)?; + let point = CoordinatePair::parse(context, input)?; + input.expect_ident_matching("of")?; + let rx = LengthPercentage::parse(context, input)?; + let ry = input + .try_parse(|i| LengthPercentage::parse(context, i)) + .unwrap_or(rx.clone()); + let radii = CoordinatePair::new(rx, ry); + + // [<arc-sweep> || <arc-size> || rotate <angle>]? + let mut arc_sweep = None; + let mut arc_size = None; + let mut rotate = None; + loop { + if arc_sweep.is_none() { + arc_sweep = input.try_parse(ArcSweep::parse).ok(); + } + + if arc_size.is_none() { + arc_size = input.try_parse(ArcSize::parse).ok(); + if arc_size.is_some() { + continue; + } + } + + if rotate.is_none() + && input + .try_parse(|i| i.expect_ident_matching("rotate")) + .is_ok() + { + rotate = Some(Angle::parse(context, input)?); + continue; + } + break; + } + Self::Arc { + by_to, + point, + radii, + arc_sweep: arc_sweep.unwrap_or(ArcSweep::Ccw), + arc_size: arc_size.unwrap_or(ArcSize::Small), + rotate: rotate.unwrap_or(Angle::zero()), + } + }, + }) + } +} + +impl Parse for generic::CoordinatePair<LengthPercentage> { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result<Self, ParseError<'i>> { + let x = LengthPercentage::parse(context, input)?; + let y = LengthPercentage::parse(context, input)?; + Ok(Self::new(x, y)) + } +} diff --git a/servo/components/style/values/specified/box.rs b/servo/components/style/values/specified/box.rs index 8414591c2b..ee50227504 100644 --- a/servo/components/style/values/specified/box.rs +++ b/servo/components/style/values/specified/box.rs @@ -19,12 +19,12 @@ use std::fmt::{self, Write}; use style_traits::{CssWriter, KeywordsCollectFn, ParseError}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; -#[cfg(not(feature = "servo-layout-2020"))] +#[cfg(not(feature = "servo"))] fn flexbox_enabled() -> bool { true } -#[cfg(feature = "servo-layout-2020")] +#[cfg(feature = "servo")] fn flexbox_enabled() -> bool { servo_config::prefs::pref_map() .get("layout.flexbox.enabled") @@ -42,9 +42,7 @@ pub enum DisplayOutside { None = 0, Inline, Block, - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] TableCaption, - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] InternalTable, #[cfg(feature = "gecko")] InternalRuby, @@ -55,28 +53,19 @@ pub enum DisplayOutside { #[repr(u8)] pub enum DisplayInside { None = 0, - #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))] Contents, Flow, FlowRoot, Flex, #[cfg(feature = "gecko")] Grid, - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] Table, - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] TableRowGroup, - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] TableColumn, - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] TableColumnGroup, - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] TableHeaderGroup, - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] TableFooterGroup, - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] TableRow, - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] TableCell, #[cfg(feature = "gecko")] Ruby, @@ -145,7 +134,6 @@ impl Display { /// ::new() inlined so cbindgen can use it pub const None: Self = Self(((DisplayOutside::None as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::None as u16); - #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))] pub const Contents: Self = Self( ((DisplayOutside::None as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Contents as u16, ); @@ -170,14 +158,11 @@ impl Display { #[cfg(feature = "gecko")] pub const InlineGrid: Self = Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Grid as u16); - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] pub const Table: Self = Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Table as u16); - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] pub const InlineTable: Self = Self( ((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Table as u16, ); - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] pub const TableCaption: Self = Self( ((DisplayOutside::TableCaption as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16, ); @@ -195,37 +180,30 @@ impl Display { // Internal table boxes. - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] pub const TableRowGroup: Self = Self( ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::TableRowGroup as u16, ); - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] pub const TableHeaderGroup: Self = Self( ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::TableHeaderGroup as u16, ); - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] pub const TableFooterGroup: Self = Self( ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::TableFooterGroup as u16, ); - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] pub const TableColumn: Self = Self( ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::TableColumn as u16, ); - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] pub const TableColumnGroup: Self = Self( ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::TableColumnGroup as u16, ); - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] pub const TableRow: Self = Self( ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::TableRow as u16, ); - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] pub const TableCell: Self = Self( ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::TableCell as u16, @@ -336,7 +314,6 @@ impl Display { pub fn is_atomic_inline_level(&self) -> bool { match *self { Display::InlineBlock | Display::InlineFlex => true, - #[cfg(any(feature = "servo-layout-2013"))] Display::InlineTable => true, _ => false, } @@ -373,7 +350,6 @@ impl Display { /// /// Also used for :root style adjustments. pub fn equivalent_block_display(&self, _is_root_element: bool) -> Self { - #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))] { // Special handling for `contents` and `list-item`s on the root element. if _is_root_element && (self.is_contents() || self.is_list_item()) { @@ -392,7 +368,6 @@ impl Display { Display::from3(DisplayOutside::Block, inside, self.is_list_item()) }, DisplayOutside::Block | DisplayOutside::None => *self, - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] _ => Display::Block, } } @@ -419,7 +394,6 @@ impl Display { #[inline] pub fn is_contents(&self) -> bool { match *self { - #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))] Display::Contents => true, _ => false, } @@ -444,30 +418,20 @@ impl DisplayKeyword { use self::DisplayKeyword::*; Ok(try_match_ident_ignore_ascii_case! { input, "none" => Full(Display::None), - #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))] "contents" => Full(Display::Contents), "inline-block" => Full(Display::InlineBlock), - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] "inline-table" => Full(Display::InlineTable), "-webkit-flex" if flexbox_enabled() => Full(Display::Flex), "inline-flex" | "-webkit-inline-flex" if flexbox_enabled() => Full(Display::InlineFlex), #[cfg(feature = "gecko")] "inline-grid" => Full(Display::InlineGrid), - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] "table-caption" => Full(Display::TableCaption), - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] "table-row-group" => Full(Display::TableRowGroup), - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] "table-header-group" => Full(Display::TableHeaderGroup), - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] "table-footer-group" => Full(Display::TableFooterGroup), - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] "table-column" => Full(Display::TableColumn), - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] "table-column-group" => Full(Display::TableColumnGroup), - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] "table-row" => Full(Display::TableRow), - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] "table-cell" => Full(Display::TableCell), #[cfg(feature = "gecko")] "ruby-base" => Full(Display::RubyBase), @@ -493,9 +457,7 @@ impl DisplayKeyword { /// https://drafts.csswg.org/css-display/#typedef-display-inside "flow" => Inside(DisplayInside::Flow), "flex" if flexbox_enabled() => Inside(DisplayInside::Flex), - #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))] "flow-root" => Inside(DisplayInside::FlowRoot), - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] "table" => Inside(DisplayInside::Table), #[cfg(feature = "gecko")] "grid" => Inside(DisplayInside::Grid), @@ -517,13 +479,11 @@ impl ToCss for Display { Display::InlineBlock => dest.write_str("inline-block"), #[cfg(feature = "gecko")] Display::WebkitInlineBox => dest.write_str("-webkit-inline-box"), - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] Display::TableCaption => dest.write_str("table-caption"), _ => match (outside, inside) { #[cfg(feature = "gecko")] (DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"), (DisplayOutside::Inline, DisplayInside::Flex) => dest.write_str("inline-flex"), - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] (DisplayOutside::Inline, DisplayInside::Table) => dest.write_str("inline-table"), #[cfg(feature = "gecko")] (DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str("block ruby"), @@ -1607,18 +1567,12 @@ pub enum Appearance { TabScrollArrowBack, #[parse(condition = "ParserContext::chrome_rules_enabled")] TabScrollArrowForward, - /// A toolbar in an application window. - #[parse(condition = "ParserContext::chrome_rules_enabled")] - Toolbar, /// A single toolbar button (with no associated dropdown). #[parse(condition = "ParserContext::chrome_rules_enabled")] Toolbarbutton, /// The dropdown portion of a toolbar button #[parse(condition = "ParserContext::chrome_rules_enabled")] ToolbarbuttonDropdown, - /// The toolbox that contains the toolbars. - #[parse(condition = "ParserContext::chrome_rules_enabled")] - Toolbox, /// A tooltip. #[parse(condition = "ParserContext::chrome_rules_enabled")] Tooltip, diff --git a/servo/components/style/values/specified/color.rs b/servo/components/style/values/specified/color.rs index 3694b4e9bc..f823ba7d30 100644 --- a/servo/components/style/values/specified/color.rs +++ b/servo/components/style/values/specified/color.rs @@ -7,21 +7,20 @@ use super::AllowQuirks; use crate::color::component::ColorComponent; use crate::color::convert::normalize_hue; -use crate::color::parsing::{self, FromParsedColor, NumberOrAngle, NumberOrPercentage}; +use crate::color::parsing::{ + self, ColorParser, FromParsedColor, NumberOrAngle, NumberOrPercentage, +}; use crate::color::{mix::ColorInterpolationMethod, AbsoluteColor, ColorSpace}; use crate::media_queries::Device; use crate::parser::{Parse, ParserContext}; use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue}; -use crate::values::generics::calc::CalcUnits; use crate::values::generics::color::{ ColorMixFlags, GenericCaretColor, GenericColorMix, GenericColorOrAuto, }; -use crate::values::specified::calc::{CalcNode, Leaf}; use crate::values::specified::Percentage; use crate::values::{normalize, CustomIdent}; use cssparser::color::OPAQUE; use cssparser::{color::PredefinedColorSpace, BasicParseErrorKind, ParseErrorKind, Parser, Token}; -use itoa; use std::fmt::{self, Write}; use std::io::Write as IoWrite; use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind}; @@ -623,156 +622,6 @@ impl FromParsedColor for Color { } } -struct ColorParser<'a, 'b: 'a>(&'a ParserContext<'b>); - -impl<'a, 'b: 'a, 'i: 'a> parsing::ColorParser<'i> for ColorParser<'a, 'b> { - type Output = Color; - - fn parse_number_or_angle<'t>( - &self, - input: &mut Parser<'i, 't>, - allow_none: bool, - ) -> Result<ColorComponent<NumberOrAngle>, ParseError<'i>> { - use crate::values::specified::Angle; - - let location = input.current_source_location(); - let token = input.next()?.clone(); - Ok(match token { - Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => { - ColorComponent::None - }, - Token::Dimension { - value, ref unit, .. - } => { - let angle = Angle::parse_dimension(value, unit, /* from_calc = */ false); - - let degrees = match angle { - Ok(angle) => angle.degrees(), - Err(()) => return Err(location.new_unexpected_token_error(token.clone())), - }; - - ColorComponent::Value(NumberOrAngle::Angle { degrees }) - }, - Token::Number { value, .. } => ColorComponent::Value(NumberOrAngle::Number { value }), - Token::Function(ref name) => { - let function = CalcNode::math_function(self.0, name, location)?; - let node = CalcNode::parse(self.0, input, function, CalcUnits::ANGLE)?; - - // If we can resolve the calc node, then use the value. - match node.resolve() { - Ok(Leaf::Number(value)) => { - ColorComponent::Value(NumberOrAngle::Number { value }) - }, - Ok(Leaf::Angle(angle)) => ColorComponent::Value(NumberOrAngle::Angle { - degrees: angle.degrees(), - }), - _ => { - return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - }, - } - }, - t => return Err(location.new_unexpected_token_error(t)), - }) - } - - fn parse_percentage<'t>( - &self, - input: &mut Parser<'i, 't>, - allow_none: bool, - ) -> Result<ColorComponent<f32>, ParseError<'i>> { - let location = input.current_source_location(); - - Ok(match *input.next()? { - Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => { - ColorComponent::None - }, - Token::Percentage { unit_value, .. } => ColorComponent::Value(unit_value), - Token::Function(ref name) => { - let function = CalcNode::math_function(self.0, name, location)?; - let node = CalcNode::parse(self.0, input, function, CalcUnits::PERCENTAGE)?; - - // If we can resolve the calc node, then use the value. - let Ok(resolved_leaf) = node.resolve() else { - return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - }; - if let Leaf::Percentage(value) = resolved_leaf { - ColorComponent::Value(value) - } else { - return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - }, - ref t => return Err(location.new_unexpected_token_error(t.clone())), - }) - } - - fn parse_number<'t>( - &self, - input: &mut Parser<'i, 't>, - allow_none: bool, - ) -> Result<ColorComponent<f32>, ParseError<'i>> { - let location = input.current_source_location(); - - Ok(match *input.next()? { - Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => { - ColorComponent::None - }, - Token::Number { value, .. } => ColorComponent::Value(value), - Token::Function(ref name) => { - let function = CalcNode::math_function(self.0, name, location)?; - let node = CalcNode::parse(self.0, input, function, CalcUnits::empty())?; - - // If we can resolve the calc node, then use the value. - let Ok(resolved_leaf) = node.resolve() else { - return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - }; - if let Leaf::Number(value) = resolved_leaf { - ColorComponent::Value(value) - } else { - return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - }, - ref t => return Err(location.new_unexpected_token_error(t.clone())), - }) - } - - fn parse_number_or_percentage<'t>( - &self, - input: &mut Parser<'i, 't>, - allow_none: bool, - ) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>> { - let location = input.current_source_location(); - - Ok(match *input.next()? { - Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => { - ColorComponent::None - }, - Token::Number { value, .. } => { - ColorComponent::Value(NumberOrPercentage::Number { value }) - }, - Token::Percentage { unit_value, .. } => { - ColorComponent::Value(NumberOrPercentage::Percentage { unit_value }) - }, - Token::Function(ref name) => { - let function = CalcNode::math_function(self.0, name, location)?; - let node = CalcNode::parse(self.0, input, function, CalcUnits::PERCENTAGE)?; - - // If we can resolve the calc node, then use the value. - let Ok(resolved_leaf) = node.resolve() else { - return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - }; - if let Leaf::Percentage(unit_value) = resolved_leaf { - ColorComponent::Value(NumberOrPercentage::Percentage { unit_value }) - } else if let Leaf::Number(value) = resolved_leaf { - ColorComponent::Value(NumberOrPercentage::Number { value }) - } else { - return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - }, - ref t => return Err(location.new_unexpected_token_error(t.clone())), - }) - } -} - /// Whether to preserve authored colors during parsing. That's useful only if we /// plan to serialize the color back. #[derive(Copy, Clone)] @@ -809,7 +658,7 @@ impl Color { }, }; - let color_parser = ColorParser(&*context); + let color_parser = ColorParser { context: &context }; match input.try_parse(|i| parsing::parse_color_with(&color_parser, i)) { Ok(mut color) => { if let Color::Absolute(ref mut absolute) = color { diff --git a/servo/components/style/values/specified/counters.rs b/servo/components/style/values/specified/counters.rs index 9d8261ce6c..7760be91d7 100644 --- a/servo/components/style/values/specified/counters.rs +++ b/servo/components/style/values/specified/counters.rs @@ -4,7 +4,7 @@ //! Specified types for counter properties. -#[cfg(feature = "servo-layout-2013")] +#[cfg(feature = "servo")] use crate::computed_values::list_style_type::T as ListStyleType; use crate::parser::{Parse, ParserContext}; use crate::values::generics::counters as generics; @@ -12,12 +12,10 @@ use crate::values::generics::counters::CounterPair; #[cfg(feature = "gecko")] use crate::values::generics::CounterStyle; use crate::values::specified::image::Image; -#[cfg(any(feature = "gecko", feature = "servo-layout-2020"))] use crate::values::specified::Attr; use crate::values::specified::Integer; use crate::values::CustomIdent; use cssparser::{Parser, Token}; -#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] use selectors::parser::SelectorParseErrorKind; use style_traits::{ParseError, StyleParseErrorKind}; @@ -151,7 +149,7 @@ pub type Content = generics::GenericContent<Image>; pub type ContentItem = generics::GenericContentItem<Image>; impl Content { - #[cfg(feature = "servo-layout-2013")] + #[cfg(feature = "servo")] fn parse_counter_style(_: &ParserContext, input: &mut Parser) -> ListStyleType { input .try_parse(|input| { @@ -197,7 +195,6 @@ impl Parse for Content { let mut content = vec![]; let mut has_alt_content = false; loop { - #[cfg(any(feature = "gecko", feature = "servo-layout-2020"))] { if let Ok(image) = input.try_parse(|i| Image::parse_forbid_none(context, i)) { content.push(generics::ContentItem::Image(image)); @@ -212,13 +209,11 @@ impl Parse for Content { }, Ok(&Token::Function(ref name)) => { let result = match_ignore_ascii_case! { &name, - #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] "counter" => input.parse_nested_block(|input| { let name = CustomIdent::parse(input, &[])?; let style = Content::parse_counter_style(context, input); Ok(generics::ContentItem::Counter(name, style)) }), - #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] "counters" => input.parse_nested_block(|input| { let name = CustomIdent::parse(input, &[])?; input.expect_comma()?; @@ -226,7 +221,6 @@ impl Parse for Content { let style = Content::parse_counter_style(context, input); Ok(generics::ContentItem::Counters(name, separator, style)) }), - #[cfg(any(feature = "gecko", feature = "servo-layout-2020"))] "attr" => input.parse_nested_block(|input| { Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?)) }), @@ -240,7 +234,6 @@ impl Parse for Content { }?; content.push(result); }, - #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] Ok(&Token::Ident(ref ident)) => { content.push(match_ignore_ascii_case! { &ident, "open-quote" => generics::ContentItem::OpenQuote, diff --git a/servo/components/style/values/specified/easing.rs b/servo/components/style/values/specified/easing.rs index 5e4d8ae1ea..7697e6a5d7 100644 --- a/servo/components/style/values/specified/easing.rs +++ b/servo/components/style/values/specified/easing.rs @@ -9,7 +9,7 @@ use crate::values::computed::easing::TimingFunction as ComputedTimingFunction; use crate::values::computed::{Context, ToComputedValue}; use crate::values::generics::easing::TimingFunction as GenericTimingFunction; use crate::values::generics::easing::{StepPosition, TimingKeyword}; -use crate::values::specified::{Integer, Number, Percentage}; +use crate::values::specified::{AnimationName, Integer, Number, Percentage}; use cssparser::{Delimiter, Parser, Token}; use selectors::parser::SelectorParseErrorKind; use style_traits::{ParseError, StyleParseErrorKind}; @@ -140,6 +140,15 @@ impl TimingFunction { Ok(GenericTimingFunction::LinearFunction(builder.build())) } + + /// Returns true if the name matches any keyword. + #[inline] + pub fn match_keywords(name: &AnimationName) -> bool { + if let Some(name) = name.as_atom() { + return name.with_str(|n| TimingKeyword::from_ident(n).is_ok()); + } + false + } } // We need this for converting the specified TimingFunction into computed TimingFunction without diff --git a/servo/components/style/values/specified/effects.rs b/servo/components/style/values/specified/effects.rs index 0453582768..a32390e148 100644 --- a/servo/components/style/values/specified/effects.rs +++ b/servo/components/style/values/specified/effects.rs @@ -28,7 +28,7 @@ use crate::values::specified::{Angle, Number, NumberOrPercentage}; #[cfg(feature = "servo")] use crate::values::Impossible; use crate::Zero; -use cssparser::{self, BasicParseErrorKind, Parser, Token}; +use cssparser::{BasicParseErrorKind, Parser, Token}; use style_traits::{ParseError, StyleParseErrorKind, ValueParseErrorKind}; /// A specified value for a single shadow of the `box-shadow` property. diff --git a/servo/components/style/values/specified/image.rs b/servo/components/style/values/specified/image.rs index 76bbbf85df..bedced1a27 100644 --- a/servo/components/style/values/specified/image.rs +++ b/servo/components/style/values/specified/image.rs @@ -241,7 +241,7 @@ impl Image { let function = input.expect_function()?.clone(); input.parse_nested_block(|input| { Ok(match_ignore_ascii_case! { &function, - #[cfg(feature = "servo-layout-2013")] + #[cfg(feature = "servo")] "paint" => Self::PaintWorklet(PaintWorklet::parse_args(context, input)?), "cross-fade" if cross_fade_enabled() => Self::CrossFade(Box::new(CrossFade::parse_args(context, input, cors_mode, flags)?)), #[cfg(feature = "gecko")] diff --git a/servo/components/style/values/specified/length.rs b/servo/components/style/values/specified/length.rs index d2e1d7d346..17ff43cd6f 100644 --- a/servo/components/style/values/specified/length.rs +++ b/servo/components/style/values/specified/length.rs @@ -97,9 +97,15 @@ pub enum LineHeightBase { impl FontBaseSize { /// Calculate the actual size for a given context pub fn resolve(&self, context: &Context) -> computed::FontSize { + let style = context.style(); match *self { - Self::CurrentStyle => context.style().get_font().clone_font_size(), - Self::InheritedStyle => context.style().get_parent_font().clone_font_size(), + Self::CurrentStyle => style.get_font().clone_font_size(), + Self::InheritedStyle => { + // If we're using the size from our inherited style, we still need to apply our + // own zoom. + let zoom = style.get_box().clone_zoom(); + style.get_parent_font().clone_font_size().zoom(zoom) + }, } } } @@ -351,7 +357,7 @@ impl FontRelativeLength { let reference_size = if context.builder.is_root_element || context.in_media_query { reference_font_size.computed_size() } else { - context.device().root_font_size() + context.device().root_font_size().zoom(context.builder.effective_zoom) }; (reference_size, length) }, @@ -394,19 +400,19 @@ impl FontRelativeLength { // When specified on the root element, the rlh units refer // to the initial values of font and line-height properties. // - let reference_size: CSSPixelLength = - if context.builder.is_root_element || context.in_media_query { - context - .device() - .calc_line_height( - &context.default_style().get_font(), - context.style().writing_mode, - None, - ) - .0 - } else { - context.device().root_line_height() - }; + let reference_size = if context.builder.is_root_element || context.in_media_query { + context + .device() + .calc_line_height( + &context.default_style().get_font(), + context.style().writing_mode, + None, + ) + .0 + } else { + context.device().root_line_height() + }; + let reference_size = reference_size.zoom(context.builder.effective_zoom); (reference_size, length) }, } @@ -668,7 +674,7 @@ impl ViewportPercentageLength { pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength { let (variant, unit, factor) = self.unpack(); let size = context.viewport_size_for_viewport_unit_resolution(variant); - let length = match unit { + let length: app_units::Au = match unit { ViewportUnit::Vw => size.width, ViewportUnit::Vh => size.height, ViewportUnit::Vmin => cmp::min(size.width, size.height), @@ -686,13 +692,15 @@ impl ViewportPercentageLength { }, }; + // NOTE: This is in app units! + let length = context.builder.effective_zoom.zoom(length.0 as f32); + // FIXME: Bug 1396535, we need to fix the extremely small viewport length for transform. - // See bug 989802. We truncate so that adding multiple viewport units - // that add up to 100 does not overflow due to rounding differences. - // We convert appUnits to CSS px manually here to avoid premature clamping by - // going through the Au type. + // See bug 989802. We truncate so that adding multiple viewport units that add up to 100 + // does not overflow due to rounding differences. We convert appUnits to CSS px manually + // here to avoid premature clamping by going through the Au type. let trunc_scaled = - ((length.0 as f64 * factor as f64 / 100.).trunc() / AU_PER_PX as f64) as f32; + ((length as f64 * factor as f64 / 100.).trunc() / AU_PER_PX as f64) as f32; CSSPixelLength::new(crate::values::normalize(trunc_scaled)) } } @@ -797,7 +805,7 @@ impl ToComputedValue for AbsoluteLength { type ComputedValue = CSSPixelLength; fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - CSSPixelLength::new(context.builder.effective_zoom().zoom(self.to_px())).finite() + CSSPixelLength::new(self.to_px()).zoom(context.builder.effective_zoom).finite() } fn from_computed_value(computed: &Self::ComputedValue) -> Self { @@ -910,6 +918,9 @@ impl ContainerRelativeLength { .builder .add_flags(ComputedValueFlags::USES_CONTAINER_UNITS); + // TODO(emilio, bug 1894104): Need to handle zoom here, probably something like + // container_zoom - effective_zoom or so. See + // https://github.com/w3c/csswg-drafts/issues/10268 let size = context.get_container_size_query(); let (factor, container_length) = match *self { Self::Cqw(v) => (v, size.get_container_width(context)), diff --git a/servo/components/style/values/specified/motion.rs b/servo/components/style/values/specified/motion.rs index 98858c712c..15c76bc640 100644 --- a/servo/components/style/values/specified/motion.rs +++ b/servo/components/style/values/specified/motion.rs @@ -76,10 +76,6 @@ impl Parse for RayFunction { context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>> { - if !static_prefs::pref!("layout.css.motion-path-ray.enabled") { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - input.expect_function_matching("ray")?; input.parse_nested_block(|i| Self::parse_function_arguments(context, i)) } @@ -154,11 +150,8 @@ impl Parse for OffsetPathFunction { // <offset-path> = <ray()> | <url> | <basic-shape> // https://drafts.fxtf.org/motion-1/#typedef-offset-path - - if static_prefs::pref!("layout.css.motion-path-ray.enabled") { - if let Ok(ray) = input.try_parse(|i| RayFunction::parse(context, i)) { - return Ok(OffsetPathFunction::Ray(ray)); - } + if let Ok(ray) = input.try_parse(|i| RayFunction::parse(context, i)) { + return Ok(OffsetPathFunction::Ray(ray)); } if static_prefs::pref!("layout.css.motion-path-url.enabled") { @@ -167,13 +160,7 @@ impl Parse for OffsetPathFunction { } } - let allowed_shapes = if static_prefs::pref!("layout.css.motion-path-basic-shapes.enabled") { - AllowedBasicShapes::ALL - } else { - AllowedBasicShapes::PATH - }; - - BasicShape::parse(context, input, allowed_shapes, ShapeType::Outline) + BasicShape::parse(context, input, AllowedBasicShapes::ALL, ShapeType::Outline) .map(OffsetPathFunction::Shape) } } @@ -197,9 +184,7 @@ impl Parse for OffsetPath { .ok(); } - if static_prefs::pref!("layout.css.motion-path-coord-box.enabled") && - coord_box.is_none() - { + if coord_box.is_none() { coord_box = input.try_parse(CoordBox::parse).ok(); if coord_box.is_some() { continue; diff --git a/servo/components/style/values/specified/svg_path.rs b/servo/components/style/values/specified/svg_path.rs index 1eb9866dd1..56342b48bb 100644 --- a/servo/components/style/values/specified/svg_path.rs +++ b/servo/components/style/values/specified/svg_path.rs @@ -5,10 +5,13 @@ //! Specified types for SVG Path. use crate::parser::{Parse, ParserContext}; -use crate::values::animated::{lists, Animate, Procedure, ToAnimatedZero}; +use crate::values::animated::{lists, Animate, Procedure}; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; +use crate::values::generics::basic_shape::GenericShapeCommand; +use crate::values::generics::basic_shape::{ArcSize, ArcSweep, ByTo, CoordinatePair}; use crate::values::CSSFloat; use cssparser::Parser; +use num_traits::FromPrimitive; use std::fmt::{self, Write}; use std::iter::{Cloned, Peekable}; use std::slice; @@ -70,6 +73,7 @@ impl SVGPathData { #[cfg(feature = "gecko")] pub fn decode_from_f32_array(path: &[f32]) -> Result<Self, ()> { use crate::gecko_bindings::structs::dom::SVGPathSeg_Binding::*; + use crate::values::generics::basic_shape::GenericShapeCommand::*; let mut result: Vec<PathCommand> = Vec::new(); let mut i: usize = 0; @@ -80,85 +84,84 @@ impl SVGPathData { let seg_type = path[i].to_bits() as u16; i = i + 1; match seg_type { - PATHSEG_CLOSEPATH => result.push(PathCommand::ClosePath), + PATHSEG_CLOSEPATH => result.push(Close), PATHSEG_MOVETO_ABS | PATHSEG_MOVETO_REL => { debug_assert!(i + 1 < path.len()); - result.push(PathCommand::MoveTo { + result.push(Move { point: CoordPair::new(path[i], path[i + 1]), - absolute: IsAbsolute::new(seg_type == PATHSEG_MOVETO_ABS), + by_to: ByTo::new(seg_type == PATHSEG_MOVETO_ABS), }); i = i + 2; }, PATHSEG_LINETO_ABS | PATHSEG_LINETO_REL => { debug_assert!(i + 1 < path.len()); - result.push(PathCommand::LineTo { + result.push(Line { point: CoordPair::new(path[i], path[i + 1]), - absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_ABS), + by_to: ByTo::new(seg_type == PATHSEG_LINETO_ABS), }); i = i + 2; }, PATHSEG_CURVETO_CUBIC_ABS | PATHSEG_CURVETO_CUBIC_REL => { debug_assert!(i + 5 < path.len()); - result.push(PathCommand::CurveTo { + result.push(CubicCurve { control1: CoordPair::new(path[i], path[i + 1]), control2: CoordPair::new(path[i + 2], path[i + 3]), point: CoordPair::new(path[i + 4], path[i + 5]), - absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_ABS), + by_to: ByTo::new(seg_type == PATHSEG_CURVETO_CUBIC_ABS), }); i = i + 6; }, PATHSEG_CURVETO_QUADRATIC_ABS | PATHSEG_CURVETO_QUADRATIC_REL => { debug_assert!(i + 3 < path.len()); - result.push(PathCommand::QuadBezierCurveTo { + result.push(QuadCurve { control1: CoordPair::new(path[i], path[i + 1]), point: CoordPair::new(path[i + 2], path[i + 3]), - absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_ABS), + by_to: ByTo::new(seg_type == PATHSEG_CURVETO_QUADRATIC_ABS), }); i = i + 4; }, PATHSEG_ARC_ABS | PATHSEG_ARC_REL => { debug_assert!(i + 6 < path.len()); - result.push(PathCommand::EllipticalArc { - rx: path[i], - ry: path[i + 1], - angle: path[i + 2], - large_arc_flag: ArcFlag(path[i + 3] != 0.0f32), - sweep_flag: ArcFlag(path[i + 4] != 0.0f32), + result.push(Arc { + radii: CoordPair::new(path[i], path[i + 1]), + rotate: path[i + 2], + arc_size: ArcSize::from_u8((path[i + 3] != 0.0f32) as u8).unwrap(), + arc_sweep: ArcSweep::from_u8((path[i + 4] != 0.0f32) as u8).unwrap(), point: CoordPair::new(path[i + 5], path[i + 6]), - absolute: IsAbsolute::new(seg_type == PATHSEG_ARC_ABS), + by_to: ByTo::new(seg_type == PATHSEG_ARC_ABS), }); i = i + 7; }, PATHSEG_LINETO_HORIZONTAL_ABS | PATHSEG_LINETO_HORIZONTAL_REL => { debug_assert!(i < path.len()); - result.push(PathCommand::HorizontalLineTo { + result.push(HLine { x: path[i], - absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_HORIZONTAL_ABS), + by_to: ByTo::new(seg_type == PATHSEG_LINETO_HORIZONTAL_ABS), }); i = i + 1; }, PATHSEG_LINETO_VERTICAL_ABS | PATHSEG_LINETO_VERTICAL_REL => { debug_assert!(i < path.len()); - result.push(PathCommand::VerticalLineTo { + result.push(VLine { y: path[i], - absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_VERTICAL_ABS), + by_to: ByTo::new(seg_type == PATHSEG_LINETO_VERTICAL_ABS), }); i = i + 1; }, PATHSEG_CURVETO_CUBIC_SMOOTH_ABS | PATHSEG_CURVETO_CUBIC_SMOOTH_REL => { debug_assert!(i + 3 < path.len()); - result.push(PathCommand::SmoothCurveTo { + result.push(SmoothCubic { control2: CoordPair::new(path[i], path[i + 1]), point: CoordPair::new(path[i + 2], path[i + 3]), - absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS), + by_to: ByTo::new(seg_type == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS), }); i = i + 4; }, PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS | PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL => { debug_assert!(i + 1 < path.len()); - result.push(PathCommand::SmoothQuadBezierCurveTo { + result.push(SmoothQuad { point: CoordPair::new(path[i], path[i + 1]), - absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS), + by_to: ByTo::new(seg_type == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS), }); i = i + 2; }, @@ -215,7 +218,7 @@ impl ToCss for SVGPathData { { let mut writer = SequenceWriter::new(dest, " "); for command in self.commands() { - writer.item(command)?; + writer.write_item(|inner| command.to_css_for_svg(inner))?; } } dest.write_char('"') @@ -268,79 +271,7 @@ impl ComputeSquaredDistance for SVGPathData { /// points of the Bézier curve in the spec. /// /// https://www.w3.org/TR/SVG11/paths.html#PathData -#[derive( - Animate, - Clone, - ComputeSquaredDistance, - Copy, - Debug, - Deserialize, - MallocSizeOf, - PartialEq, - Serialize, - SpecifiedValueInfo, - ToAnimatedZero, - ToComputedValue, - ToResolvedValue, - ToShmem, -)] -#[allow(missing_docs)] -#[repr(C, u8)] -pub enum PathCommand { - /// The unknown type. - /// https://www.w3.org/TR/SVG/paths.html#__svg__SVGPathSeg__PATHSEG_UNKNOWN - Unknown, - /// The "moveto" command. - MoveTo { - point: CoordPair, - absolute: IsAbsolute, - }, - /// The "lineto" command. - LineTo { - point: CoordPair, - absolute: IsAbsolute, - }, - /// The horizontal "lineto" command. - HorizontalLineTo { x: CSSFloat, absolute: IsAbsolute }, - /// The vertical "lineto" command. - VerticalLineTo { y: CSSFloat, absolute: IsAbsolute }, - /// The cubic Bézier curve command. - CurveTo { - control1: CoordPair, - control2: CoordPair, - point: CoordPair, - absolute: IsAbsolute, - }, - /// The smooth curve command. - SmoothCurveTo { - control2: CoordPair, - point: CoordPair, - absolute: IsAbsolute, - }, - /// The quadratic Bézier curve command. - QuadBezierCurveTo { - control1: CoordPair, - point: CoordPair, - absolute: IsAbsolute, - }, - /// The smooth quadratic Bézier curve command. - SmoothQuadBezierCurveTo { - point: CoordPair, - absolute: IsAbsolute, - }, - /// The elliptical arc curve command. - EllipticalArc { - rx: CSSFloat, - ry: CSSFloat, - angle: CSSFloat, - large_arc_flag: ArcFlag, - sweep_flag: ArcFlag, - point: CoordPair, - absolute: IsAbsolute, - }, - /// The "closepath" command. - ClosePath, -} +pub type PathCommand = GenericShapeCommand<CSSFloat, CSSFloat>; /// For internal SVGPath normalization. #[allow(missing_docs)] @@ -355,177 +286,157 @@ impl PathCommand { /// /// See discussion: https://github.com/w3c/svgwg/issues/321 fn normalize(&self, state: &mut PathTraversalState) -> Self { - use self::PathCommand::*; + use crate::values::generics::basic_shape::GenericShapeCommand::*; match *self { - Unknown => Unknown, - ClosePath => { + Close => { state.pos = state.subpath_start; - ClosePath + Close }, - MoveTo { - mut point, - absolute, - } => { - if !absolute.is_yes() { + Move { by_to, mut point } => { + if !by_to.is_abs() { point += state.pos; } state.pos = point; state.subpath_start = point; - MoveTo { + Move { + by_to: ByTo::To, point, - absolute: IsAbsolute::Yes, } }, - LineTo { - mut point, - absolute, - } => { - if !absolute.is_yes() { + Line { by_to, mut point } => { + if !by_to.is_abs() { point += state.pos; } state.pos = point; - LineTo { + Line { + by_to: ByTo::To, point, - absolute: IsAbsolute::Yes, } }, - HorizontalLineTo { mut x, absolute } => { - if !absolute.is_yes() { + HLine { by_to, mut x } => { + if !by_to.is_abs() { x += state.pos.x; } state.pos.x = x; - HorizontalLineTo { - x, - absolute: IsAbsolute::Yes, - } + HLine { by_to: ByTo::To, x } }, - VerticalLineTo { mut y, absolute } => { - if !absolute.is_yes() { + VLine { by_to, mut y } => { + if !by_to.is_abs() { y += state.pos.y; } state.pos.y = y; - VerticalLineTo { - y, - absolute: IsAbsolute::Yes, - } + VLine { by_to: ByTo::To, y } }, - CurveTo { + CubicCurve { + by_to, + mut point, mut control1, mut control2, - mut point, - absolute, } => { - if !absolute.is_yes() { + if !by_to.is_abs() { + point += state.pos; control1 += state.pos; control2 += state.pos; - point += state.pos; } state.pos = point; - CurveTo { + CubicCurve { + by_to: ByTo::To, + point, control1, control2, - point, - absolute: IsAbsolute::Yes, } }, - SmoothCurveTo { - mut control2, + QuadCurve { + by_to, mut point, - absolute, + mut control1, } => { - if !absolute.is_yes() { - control2 += state.pos; + if !by_to.is_abs() { point += state.pos; + control1 += state.pos; } state.pos = point; - SmoothCurveTo { - control2, + QuadCurve { + by_to: ByTo::To, point, - absolute: IsAbsolute::Yes, + control1, } }, - QuadBezierCurveTo { - mut control1, + SmoothCubic { + by_to, mut point, - absolute, + mut control2, } => { - if !absolute.is_yes() { - control1 += state.pos; + if !by_to.is_abs() { point += state.pos; + control2 += state.pos; } state.pos = point; - QuadBezierCurveTo { - control1, + SmoothCubic { + by_to: ByTo::To, point, - absolute: IsAbsolute::Yes, + control2, } }, - SmoothQuadBezierCurveTo { - mut point, - absolute, - } => { - if !absolute.is_yes() { + SmoothQuad { by_to, mut point } => { + if !by_to.is_abs() { point += state.pos; } state.pos = point; - SmoothQuadBezierCurveTo { + SmoothQuad { + by_to: ByTo::To, point, - absolute: IsAbsolute::Yes, } }, - EllipticalArc { - rx, - ry, - angle, - large_arc_flag, - sweep_flag, + Arc { + by_to, mut point, - absolute, + radii, + arc_sweep, + arc_size, + rotate, } => { - if !absolute.is_yes() { + if !by_to.is_abs() { point += state.pos; } state.pos = point; - EllipticalArc { - rx, - ry, - angle, - large_arc_flag, - sweep_flag, + Arc { + by_to: ByTo::To, point, - absolute: IsAbsolute::Yes, + radii, + arc_sweep, + arc_size, + rotate, } }, } } -} -impl ToCss for PathCommand { - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + /// The serialization of the svg path. + fn to_css_for_svg<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write, { - use self::PathCommand::*; + use crate::values::generics::basic_shape::GenericShapeCommand::*; match *self { - Unknown => dest.write_char('X'), - ClosePath => dest.write_char('Z'), - MoveTo { point, absolute } => { - dest.write_char(if absolute.is_yes() { 'M' } else { 'm' })?; + Close => dest.write_char('Z'), + Move { by_to, point } => { + dest.write_char(if by_to.is_abs() { 'M' } else { 'm' })?; dest.write_char(' ')?; point.to_css(dest) }, - LineTo { point, absolute } => { - dest.write_char(if absolute.is_yes() { 'L' } else { 'l' })?; + Line { by_to, point } => { + dest.write_char(if by_to.is_abs() { 'L' } else { 'l' })?; dest.write_char(' ')?; point.to_css(dest) }, - CurveTo { + CubicCurve { + by_to, + point, control1, control2, - point, - absolute, } => { - dest.write_char(if absolute.is_yes() { 'C' } else { 'c' })?; + dest.write_char(if by_to.is_abs() { 'C' } else { 'c' })?; dest.write_char(' ')?; control1.to_css(dest)?; dest.write_char(' ')?; @@ -533,63 +444,60 @@ impl ToCss for PathCommand { dest.write_char(' ')?; point.to_css(dest) }, - QuadBezierCurveTo { - control1, + QuadCurve { + by_to, point, - absolute, + control1, } => { - dest.write_char(if absolute.is_yes() { 'Q' } else { 'q' })?; + dest.write_char(if by_to.is_abs() { '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, + Arc { + by_to, point, - absolute, + radii, + arc_sweep, + arc_size, + rotate, } => { - dest.write_char(if absolute.is_yes() { 'A' } else { 'a' })?; - dest.write_char(' ')?; - rx.to_css(dest)?; + dest.write_char(if by_to.is_abs() { 'A' } else { 'a' })?; dest.write_char(' ')?; - ry.to_css(dest)?; + radii.to_css(dest)?; dest.write_char(' ')?; - angle.to_css(dest)?; + rotate.to_css(dest)?; dest.write_char(' ')?; - large_arc_flag.to_css(dest)?; + (arc_size as i32).to_css(dest)?; dest.write_char(' ')?; - sweep_flag.to_css(dest)?; + (arc_sweep as i32).to_css(dest)?; dest.write_char(' ')?; point.to_css(dest) }, - HorizontalLineTo { x, absolute } => { - dest.write_char(if absolute.is_yes() { 'H' } else { 'h' })?; + HLine { by_to, x } => { + dest.write_char(if by_to.is_abs() { 'H' } else { 'h' })?; dest.write_char(' ')?; x.to_css(dest) }, - VerticalLineTo { y, absolute } => { - dest.write_char(if absolute.is_yes() { 'V' } else { 'v' })?; + VLine { by_to, y } => { + dest.write_char(if by_to.is_abs() { 'V' } else { 'v' })?; dest.write_char(' ')?; y.to_css(dest) }, - SmoothCurveTo { - control2, + SmoothCubic { + by_to, point, - absolute, + control2, } => { - dest.write_char(if absolute.is_yes() { 'S' } else { 's' })?; + dest.write_char(if by_to.is_abs() { '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.is_yes() { 'T' } else { 't' })?; + SmoothQuad { by_to, point } => { + dest.write_char(if by_to.is_abs() { 'T' } else { 't' })?; dest.write_char(' ')?; point.to_css(dest) }, @@ -597,135 +505,8 @@ impl ToCss for PathCommand { } } -/// The path command absolute type. -#[allow(missing_docs)] -#[derive( - Animate, - Clone, - ComputeSquaredDistance, - Copy, - Debug, - Deserialize, - MallocSizeOf, - PartialEq, - Serialize, - SpecifiedValueInfo, - ToAnimatedZero, - ToComputedValue, - ToResolvedValue, - ToShmem, -)] -#[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 - } - - /// Return Yes if value is true. Otherwise, return No. - #[inline] - fn new(value: bool) -> Self { - if value { - IsAbsolute::Yes - } else { - IsAbsolute::No - } - } -} - /// The path coord type. -#[allow(missing_docs)] -#[derive( - AddAssign, - Animate, - Clone, - ComputeSquaredDistance, - Copy, - Debug, - Deserialize, - MallocSizeOf, - PartialEq, - Serialize, - SpecifiedValueInfo, - ToAnimatedZero, - ToComputedValue, - ToCss, - ToResolvedValue, - ToShmem, -)] -#[repr(C)] -pub struct CoordPair { - x: CSSFloat, - y: CSSFloat, -} - -impl CoordPair { - /// Create a CoordPair. - #[inline] - pub fn new(x: CSSFloat, y: CSSFloat) -> Self { - CoordPair { x, y } - } -} - -/// The EllipticalArc flag type. -#[derive( - Clone, - Copy, - Debug, - Deserialize, - MallocSizeOf, - PartialEq, - Serialize, - SpecifiedValueInfo, - ToComputedValue, - ToResolvedValue, - ToShmem, -)] -#[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 Animate for ArcFlag { - #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - (self.0 as i32) - .animate(&(other.0 as i32), procedure) - .map(|v| ArcFlag(v > 0)) - } -} - -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)) - } -} - -impl ToAnimatedZero for ArcFlag { - #[inline] - fn to_animated_zero(&self) -> Result<Self, ()> { - // The 2 ArcFlags in EllipticalArc determine which one of the 4 different arcs will be - // used. (i.e. From 4 combinations). In other words, if we change the flag, we get a - // different arc. Therefore, we return *self. - // https://svgwg.org/svg2-draft/paths.html#PathDataEllipticalArcCommands - Ok(*self) - } -} +pub type CoordPair = CoordinatePair<CSSFloat>; /// SVG Path parser. struct PathParser<'a> { @@ -736,7 +517,7 @@ struct PathParser<'a> { macro_rules! parse_arguments { ( $parser:ident, - $abs:ident, + $by_to:ident, $enum:ident, [ $para:ident => $func:ident $(, $other_para:ident => $other_func:ident)* ] ) => { @@ -747,7 +528,9 @@ macro_rules! parse_arguments { skip_comma_wsp(&mut $parser.chars); let $other_para = $other_func(&mut $parser.chars)?; )* - $parser.path.push(PathCommand::$enum { $para $(, $other_para)*, $abs }); + $parser.path.push( + PathCommand::$enum { $by_to, $para $(, $other_para)* } + ); // End of string or the next character is a possible new command. if !skip_wsp(&mut $parser.chars) || @@ -785,23 +568,23 @@ impl<'a> PathParser<'a> { } let command = self.chars.next().unwrap(); - let abs = if command.is_ascii_uppercase() { - IsAbsolute::Yes + let by_to = if command.is_ascii_uppercase() { + ByTo::To } else { - IsAbsolute::No + ByTo::By }; skip_wsp(&mut self.chars); match command { b'Z' | b'z' => self.parse_closepath(), - b'L' | b'l' => self.parse_lineto(abs), - b'H' | b'h' => self.parse_h_lineto(abs), - b'V' | b'v' => self.parse_v_lineto(abs), - b'C' | b'c' => self.parse_curveto(abs), - b'S' | b's' => self.parse_smooth_curveto(abs), - b'Q' | b'q' => self.parse_quadratic_bezier_curveto(abs), - b'T' | b't' => self.parse_smooth_quadratic_bezier_curveto(abs), - b'A' | b'a' => self.parse_elliptical_arc(abs), + b'L' | b'l' => self.parse_lineto(by_to), + b'H' | b'h' => self.parse_h_lineto(by_to), + b'V' | b'v' => self.parse_v_lineto(by_to), + b'C' | b'c' => self.parse_curveto(by_to), + b'S' | b's' => self.parse_smooth_curveto(by_to), + b'Q' | b'q' => self.parse_quadratic_bezier_curveto(by_to), + b'T' | b't' => self.parse_smooth_quadratic_bezier_curveto(by_to), + b'A' | b'a' => self.parse_elliptical_arc(by_to), _ => return Err(()), }?; } @@ -817,12 +600,8 @@ impl<'a> PathParser<'a> { skip_wsp(&mut self.chars); let point = parse_coord(&mut self.chars)?; - let absolute = if command == b'M' { - IsAbsolute::Yes - } else { - IsAbsolute::No - }; - self.path.push(PathCommand::MoveTo { point, absolute }); + let by_to = if command == b'M' { ByTo::To } else { ByTo::By }; + self.path.push(PathCommand::Move { by_to, point }); // End of string or the next character is a possible new command. if !skip_wsp(&mut self.chars) || self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) @@ -833,69 +612,74 @@ impl<'a> PathParser<'a> { // If a moveto is followed by multiple pairs of coordinates, the subsequent // pairs are treated as implicit lineto commands. - self.parse_lineto(absolute) + self.parse_lineto(by_to) } /// Parse "closepath" command. fn parse_closepath(&mut self) -> Result<(), ()> { - self.path.push(PathCommand::ClosePath); + self.path.push(PathCommand::Close); Ok(()) } /// Parse "lineto" command. - fn parse_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> { - parse_arguments!(self, absolute, LineTo, [ point => parse_coord ]) + fn parse_lineto(&mut self, by_to: ByTo) -> Result<(), ()> { + parse_arguments!(self, by_to, Line, [ point => parse_coord ]) } /// Parse horizontal "lineto" command. - fn parse_h_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> { - parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ]) + fn parse_h_lineto(&mut self, by_to: ByTo) -> Result<(), ()> { + parse_arguments!(self, by_to, HLine, [ x => parse_number ]) } /// Parse vertical "lineto" command. - fn parse_v_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> { - parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ]) + fn parse_v_lineto(&mut self, by_to: ByTo) -> Result<(), ()> { + parse_arguments!(self, by_to, VLine, [ y => parse_number ]) } /// Parse cubic Bézier curve command. - fn parse_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> { - parse_arguments!(self, absolute, CurveTo, [ + fn parse_curveto(&mut self, by_to: ByTo) -> Result<(), ()> { + parse_arguments!(self, by_to, CubicCurve, [ control1 => parse_coord, control2 => parse_coord, point => parse_coord ]) } /// Parse smooth "curveto" command. - fn parse_smooth_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> { - parse_arguments!(self, absolute, SmoothCurveTo, [ + fn parse_smooth_curveto(&mut self, by_to: ByTo) -> Result<(), ()> { + parse_arguments!(self, by_to, SmoothCubic, [ control2 => parse_coord, point => parse_coord ]) } /// Parse quadratic Bézier curve command. - fn parse_quadratic_bezier_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> { - parse_arguments!(self, absolute, QuadBezierCurveTo, [ + fn parse_quadratic_bezier_curveto(&mut self, by_to: ByTo) -> Result<(), ()> { + parse_arguments!(self, by_to, QuadCurve, [ control1 => parse_coord, point => parse_coord ]) } /// Parse smooth quadratic Bézier curveto command. - fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> { - parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ]) + fn parse_smooth_quadratic_bezier_curveto(&mut self, by_to: ByTo) -> Result<(), ()> { + parse_arguments!(self, by_to, SmoothQuad, [ point => parse_coord ]) } /// Parse elliptical arc curve command. - fn parse_elliptical_arc(&mut self, absolute: IsAbsolute) -> Result<(), ()> { + fn parse_elliptical_arc(&mut self, by_to: ByTo) -> Result<(), ()> { // Parse a flag whose value is '0' or '1'; otherwise, return Err(()). - let parse_flag = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| match iter.next() { - Some(c) if c == b'0' || c == b'1' => Ok(ArcFlag(c == b'1')), + let parse_arc_size = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| match iter.next() { + Some(c) if c == b'1' => Ok(ArcSize::Large), + Some(c) if c == b'0' => Ok(ArcSize::Small), + _ => Err(()), + }; + let parse_arc_sweep = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| match iter.next() { + Some(c) if c == b'1' => Ok(ArcSweep::Cw), + Some(c) if c == b'0' => Ok(ArcSweep::Ccw), _ => Err(()), }; - parse_arguments!(self, absolute, EllipticalArc, [ - rx => parse_number, - ry => parse_number, - angle => parse_number, - large_arc_flag => parse_flag, - sweep_flag => parse_flag, + parse_arguments!(self, by_to, Arc, [ + radii => parse_coord, + rotate => parse_number, + arc_size => parse_arc_size, + arc_sweep => parse_arc_sweep, point => parse_coord ]) } diff --git a/servo/components/style/values/specified/text.rs b/servo/components/style/values/specified/text.rs index 0e70bd26ac..e26a17ba27 100644 --- a/servo/components/style/values/specified/text.rs +++ b/servo/components/style/values/specified/text.rs @@ -454,7 +454,6 @@ pub enum TextAlignKeyword { Left, Right, Center, - #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] Justify, #[css(skip)] #[cfg(feature = "gecko")] @@ -466,11 +465,11 @@ pub enum TextAlignKeyword { MozLeft, #[cfg(feature = "gecko")] MozRight, - #[cfg(feature = "servo-layout-2013")] + #[cfg(feature = "servo")] ServoCenter, - #[cfg(feature = "servo-layout-2013")] + #[cfg(feature = "servo")] ServoLeft, - #[cfg(feature = "servo-layout-2013")] + #[cfg(feature = "servo")] ServoRight, } |