From def92d1b8e9d373e2f6f27c366d578d97d8960c6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:34:50 +0200 Subject: Merging upstream version 126.0. Signed-off-by: Daniel Baumann --- .../components/style/values/specified/animation.rs | 40 ++ .../style/values/specified/basic_shape.rs | 206 +++++++- servo/components/style/values/specified/box.rs | 50 +- servo/components/style/values/specified/color.rs | 159 +----- .../components/style/values/specified/counters.rs | 11 +- servo/components/style/values/specified/easing.rs | 11 +- servo/components/style/values/specified/effects.rs | 2 +- servo/components/style/values/specified/image.rs | 2 +- servo/components/style/values/specified/length.rs | 57 ++- servo/components/style/values/specified/motion.rs | 23 +- .../components/style/values/specified/svg_path.rs | 562 +++++++-------------- servo/components/style/values/specified/text.rs | 7 +- 12 files changed, 472 insertions(+), 658 deletions(-) (limited to 'servo/components/style/values/specified') 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; /// A specified basic shape. pub type BasicShape = generic::GenericBasicShape< + Angle, ShapePosition, LengthPercentage, NonNegativeLengthPercentage, @@ -61,6 +63,12 @@ pub type ShapeRadius = generic::ShapeRadius; /// The specified value of `Polygon`. pub type Polygon = generic::GenericPolygon; +/// The specified value of `PathOrShapeFunction`. +pub type PathOrShapeFunction = generic::GenericPathOrShapeFunction; + +/// The specified value of `ShapeCommand`. +pub type ShapeCommand = generic::GenericShapeCommand; + /// 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 `` 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> { - 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> { 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 { + /// Parse the inner arguments of a `shape` function. + /// shape() = shape(? from , #) + fn parse_function_arguments<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + shape_type: ShapeType, + ) -> Result> { + 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 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 . + 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> { + use crate::values::generics::basic_shape::{ArcSize, ArcSweep, ByTo, CoordinatePair}; + + // = | | | + // | | | 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); + + // [ || || rotate ]? + 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 { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + 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, 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, 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, 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, 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; pub type ContentItem = generics::GenericContentItem; 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> { - 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 { // = | | // 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 { use crate::gecko_bindings::structs::dom::SVGPathSeg_Binding::*; + use crate::values::generics::basic_shape::GenericShapeCommand::*; let mut result: Vec = 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; /// 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(&self, dest: &mut CssWriter) -> fmt::Result + /// The serialization of the svg path. + fn to_css_for_svg(&self, dest: &mut CssWriter) -> 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(&self, dest: &mut CssWriter) -> 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.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 { - (self.0 as i32).compute_squared_distance(&(other.0 as i32)) - } -} - -impl ToAnimatedZero for ArcFlag { - #[inline] - fn to_animated_zero(&self) -> Result { - // 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; /// 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>>| match iter.next() { - Some(c) if c == b'0' || c == b'1' => Ok(ArcFlag(c == b'1')), + let parse_arc_size = |iter: &mut Peekable>>| 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>>| 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, } -- cgit v1.2.3