diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index 5fdd8cee1c6..cdb05d3a561 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -2076,120 +2076,13 @@ ${helpers.single_keyword("transform-style", flags="CREATES_STACKING_CONTEXT FIXPOS_CB", animation_value_type="discrete")} -<%helpers:longhand name="transform-origin" animation_value_type="ComputedValue" extra_prefixes="moz webkit" boxed="True" - spec="https://drafts.csswg.org/css-transforms/#transform-origin-property"> - use app_units::Au; - use std::fmt; - use style_traits::ToCss; - use values::specified::{NoCalcLength, LengthOrPercentage, Percentage}; - - pub mod computed_value { - use properties::animated_properties::Animatable; - use values::computed::{Length, LengthOrPercentage}; - - #[derive(Clone, Copy, Debug, PartialEq)] - #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub struct T { - pub horizontal: LengthOrPercentage, - pub vertical: LengthOrPercentage, - pub depth: Length, - } - - impl Animatable for T { - #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) - -> Result { - Ok(T { - horizontal: try!(self.horizontal.add_weighted(&other.horizontal, - self_portion, other_portion)), - vertical: try!(self.vertical.add_weighted(&other.vertical, - self_portion, other_portion)), - depth: try!(self.depth.add_weighted(&other.depth, self_portion, other_portion)), - }) - } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - Ok(try!(self.horizontal.compute_squared_distance(&other.horizontal)) + - try!(self.vertical.compute_squared_distance(&other.vertical)) + - try!(self.depth.compute_squared_distance(&other.depth))) - } - } - } - - #[derive(Clone, Debug, HasViewportPercentage, PartialEq)] - #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub struct SpecifiedValue { - horizontal: LengthOrPercentage, - vertical: LengthOrPercentage, - depth: NoCalcLength, - } - - impl ToCss for computed_value::T { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - try!(self.horizontal.to_css(dest)); - try!(dest.write_str(" ")); - try!(self.vertical.to_css(dest)); - try!(dest.write_str(" ")); - self.depth.to_css(dest) - } - } - - impl ToCss for SpecifiedValue { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - try!(self.horizontal.to_css(dest)); - try!(dest.write_str(" ")); - try!(self.vertical.to_css(dest)); - try!(dest.write_str(" ")); - self.depth.to_css(dest) - } - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T { - horizontal: computed::LengthOrPercentage::Percentage(0.5), - vertical: computed::LengthOrPercentage::Percentage(0.5), - depth: Au(0), - } - } - - pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { - let result = try!(super::parse_origin(context, input)); - Ok(SpecifiedValue { - horizontal: result.horizontal.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))), - vertical: result.vertical.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))), - depth: result.depth.unwrap_or(NoCalcLength::zero()), - }) - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value(&self, context: &Context) -> computed_value::T { - computed_value::T { - horizontal: self.horizontal.to_computed_value(context), - vertical: self.vertical.to_computed_value(context), - depth: self.depth.to_computed_value(context), - } - } - - #[inline] - fn from_computed_value(computed: &computed_value::T) -> Self { - SpecifiedValue { - horizontal: ToComputedValue::from_computed_value(&computed.horizontal), - vertical: ToComputedValue::from_computed_value(&computed.vertical), - depth: ToComputedValue::from_computed_value(&computed.depth), - } - } - } - +${helpers.predefined_type("transform-origin", + "TransformOrigin", + "computed::TransformOrigin::initial_value()", + animation_value_type="ComputedValue", + extra_prefixes="moz webkit", + boxed=True, + spec="https://drafts.csswg.org/css-transforms/#transform-origin-property")} // FIXME: `size` and `content` values are not implemented and `strict` is implemented // like `content`(layout style paint) in gecko. We should implement `size` and `content`, diff --git a/components/style/properties/longhand/effects.mako.rs b/components/style/properties/longhand/effects.mako.rs index 32c96bd39ac..5ebf602262a 100644 --- a/components/style/properties/longhand/effects.mako.rs +++ b/components/style/properties/longhand/effects.mako.rs @@ -429,96 +429,6 @@ ${helpers.predefined_type("clip", } -pub struct OriginParseResult { - pub horizontal: Option, - pub vertical: Option, - pub depth: Option -} - -pub fn parse_origin(context: &ParserContext, input: &mut Parser) -> Result { - use values::specified::{LengthOrPercentage, Percentage}; - let (mut horizontal, mut vertical, mut depth, mut horizontal_is_center) = (None, None, None, false); - loop { - if let Err(_) = input.try(|input| { - let token = try!(input.expect_ident()); - match_ignore_ascii_case! { - &token, - "left" => { - if horizontal.is_none() { - horizontal = Some(LengthOrPercentage::Percentage(Percentage(0.0))) - } else if horizontal_is_center && vertical.is_none() { - vertical = Some(LengthOrPercentage::Percentage(Percentage(0.5))); - horizontal = Some(LengthOrPercentage::Percentage(Percentage(0.0))); - } else { - return Err(()) - } - }, - "center" => { - if horizontal.is_none() { - horizontal_is_center = true; - horizontal = Some(LengthOrPercentage::Percentage(Percentage(0.5))) - } else if vertical.is_none() { - vertical = Some(LengthOrPercentage::Percentage(Percentage(0.5))) - } else { - return Err(()) - } - }, - "right" => { - if horizontal.is_none() { - horizontal = Some(LengthOrPercentage::Percentage(Percentage(1.0))) - } else if horizontal_is_center && vertical.is_none() { - vertical = Some(LengthOrPercentage::Percentage(Percentage(0.5))); - horizontal = Some(LengthOrPercentage::Percentage(Percentage(1.0))); - } else { - return Err(()) - } - }, - "top" => { - if vertical.is_none() { - vertical = Some(LengthOrPercentage::Percentage(Percentage(0.0))) - } else { - return Err(()) - } - }, - "bottom" => { - if vertical.is_none() { - vertical = Some(LengthOrPercentage::Percentage(Percentage(1.0))) - } else { - return Err(()) - } - }, - _ => return Err(()) - } - Ok(()) - }) { - match input.try(|input| LengthOrPercentage::parse(context, input)) { - Ok(value) => { - if horizontal.is_none() { - horizontal = Some(value); - } else if vertical.is_none() { - vertical = Some(value); - } else if let LengthOrPercentage::Length(length) = value { - depth = Some(length); - } else { - break; - } - } - _ => break, - } - } - } - - if horizontal.is_some() || vertical.is_some() { - Ok(OriginParseResult { - horizontal: horizontal, - vertical: vertical, - depth: depth, - }) - } else { - Err(()) - } -} - ${helpers.single_keyword("mix-blend-mode", """normal multiply screen overlay darken lighten color-dodge color-burn hard-light soft-light difference exclusion hue diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 1e96ffa01a8..200ade9709d 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -130,10 +130,6 @@ macro_rules! expanded { /// A module with all the code for longhand properties. #[allow(missing_docs)] pub mod longhands { - use cssparser::Parser; - use parser::{Parse, ParserContext}; - use values::specified; - <%include file="/longhand/background.mako.rs" /> <%include file="/longhand/border.mako.rs" /> <%include file="/longhand/box.mako.rs" /> diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 1a295217438..5931d0e2fb5 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -38,6 +38,7 @@ pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrP pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone}; pub use self::length::{MaxLength, MozLength}; pub use self::position::Position; +pub use self::transform::TransformOrigin; pub mod background; pub mod basic_shape; @@ -46,6 +47,7 @@ pub mod image; pub mod length; pub mod position; pub mod rect; +pub mod transform; /// A `Context` is all the data a specified value could ever need to compute /// itself and be transformed to a computed value. diff --git a/components/style/values/computed/transform.rs b/components/style/values/computed/transform.rs new file mode 100644 index 00000000000..21d3c387a8b --- /dev/null +++ b/components/style/values/computed/transform.rs @@ -0,0 +1,49 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Computed types for CSS values that are related to transformations. + +use properties::animated_properties::Animatable; +use values::computed::{Length, LengthOrPercentage}; +use values::generics::transform::TransformOrigin as GenericTransformOrigin; + +/// The computed value of a CSS `` +pub type TransformOrigin = GenericTransformOrigin; + +impl TransformOrigin { + /// Returns the initial computed value for `transform-origin`. + #[inline] + pub fn initial_value() -> Self { + Self::new( + LengthOrPercentage::Percentage(0.5), + LengthOrPercentage::Percentage(0.5), + Length::from_px(0) + ) + } +} + +impl Animatable for TransformOrigin { + #[inline] + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + Ok(Self::new( + self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?, + self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?, + self.depth.add_weighted(&other.depth, self_portion, other_portion)?, + )) + } + + #[inline] + fn compute_distance(&self, other: &Self) -> Result { + self.compute_squared_distance(other).map(f64::sqrt) + } + + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + Ok( + self.horizontal.compute_squared_distance(&other.horizontal)? + + self.vertical.compute_squared_distance(&other.vertical)? + + self.depth.compute_squared_distance(&other.depth)? + ) + } +} diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index 1d25f3b3845..441941d4b2e 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -19,6 +19,7 @@ pub mod grid; pub mod image; pub mod position; pub mod rect; +pub mod transform; // https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type define_css_keyword_enum! { SymbolsType: diff --git a/components/style/values/generics/transform.rs b/components/style/values/generics/transform.rs new file mode 100644 index 00000000000..7cbc9ce4521 --- /dev/null +++ b/components/style/values/generics/transform.rs @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Generic types for CSS values that are related to transformations. + +use std::fmt; +use style_traits::ToCss; + +/// A generic transform origin. +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] +pub struct TransformOrigin { + /// The horizontal origin. + pub horizontal: H, + /// The vertical origin. + pub vertical: V, + /// The depth. + pub depth: Depth, +} + +impl TransformOrigin { + /// Returns a new transform origin. + pub fn new(horizontal: H, vertical: V, depth: D) -> Self { + Self { + horizontal: horizontal, + vertical: vertical, + depth: depth, + } + } +} + +impl ToCss for TransformOrigin + where H: ToCss, V: ToCss, D: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result + where W: fmt::Write, + { + self.horizontal.to_css(dest)?; + dest.write_str(" ")?; + self.vertical.to_css(dest)?; + dest.write_str(" ")?; + self.depth.to_css(dest) + } +} diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 158b0664faa..3913c5914e8 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -42,6 +42,7 @@ pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercent pub use self::length::{LengthOrPercentageOrNone, LengthOrPercentageOrAutoOrContent, NoCalcLength}; pub use self::length::{MaxLength, MozLength}; pub use self::position::{Position, PositionComponent}; +pub use self::transform::TransformOrigin; #[cfg(feature = "gecko")] pub mod align; @@ -55,6 +56,7 @@ pub mod image; pub mod length; pub mod position; pub mod rect; +pub mod transform; /// Common handling for the specified value CSS url() values. pub mod url { diff --git a/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs new file mode 100644 index 00000000000..9b2e10ed95b --- /dev/null +++ b/components/style/values/specified/transform.rs @@ -0,0 +1,132 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Specified types for CSS values that are related to transformations. + +use cssparser::Parser; +use parser::{Parse, ParserContext}; +use std::fmt; +use style_traits::ToCss; +use values::computed::{LengthOrPercentage as ComputedLengthOrPercentage, Context, ToComputedValue}; +use values::generics::transform::TransformOrigin as GenericTransformOrigin; +use values::specified::length::{Length, LengthOrPercentage}; +use values::specified::position::{Side, X, Y}; + +/// The specified value of a CSS `` +pub type TransformOrigin = GenericTransformOrigin, OriginComponent, Length>; + +/// The specified value of a component of a CSS ``. +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Debug, HasViewportPercentage, PartialEq)] +pub enum OriginComponent { + /// `center` + Center, + /// `` + Length(LengthOrPercentage), + /// `` + Side(S), +} + +impl Parse for TransformOrigin { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + let parse_depth = |input: &mut Parser| { + input.try(|i| Length::parse(context, i)).unwrap_or(Length::from_px(0.)) + }; + match input.try(|i| OriginComponent::parse(context, i)) { + Ok(x_origin @ OriginComponent::Center) => { + if let Ok(y_origin) = input.try(|i| OriginComponent::parse(context, i)) { + let depth = parse_depth(input); + return Ok(Self::new(x_origin, y_origin, depth)); + } + let y_origin = OriginComponent::Center; + if let Ok(x_keyword) = input.try(X::parse) { + let x_origin = OriginComponent::Side(x_keyword); + let depth = parse_depth(input); + return Ok(Self::new(x_origin, y_origin, depth)); + } + let depth = Length::from_px(0.); + return Ok(Self::new(x_origin, y_origin, depth)); + }, + Ok(x_origin) => { + if let Ok(y_origin) = input.try(|i| OriginComponent::parse(context, i)) { + let depth = parse_depth(input); + return Ok(Self::new(x_origin, y_origin, depth)); + } + let y_origin = OriginComponent::Center; + let depth = Length::from_px(0.); + return Ok(Self::new(x_origin, y_origin, depth)); + }, + Err(_) => {}, + } + let y_keyword = Y::parse(input)?; + let y_origin = OriginComponent::Side(y_keyword); + if let Ok(x_keyword) = input.try(X::parse) { + let x_origin = OriginComponent::Side(x_keyword); + let depth = parse_depth(input); + return Ok(Self::new(x_origin, y_origin, depth)); + } + if input.try(|i| i.expect_ident_matching("center")).is_ok() { + let x_origin = OriginComponent::Center; + let depth = parse_depth(input); + return Ok(Self::new(x_origin, y_origin, depth)); + } + let x_origin = OriginComponent::Center; + let depth = Length::from_px(0.); + Ok(Self::new(x_origin, y_origin, depth)) + } +} + +impl Parse for OriginComponent + where S: Parse, +{ + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + if input.try(|i| i.expect_ident_matching("center")).is_ok() { + return Ok(OriginComponent::Center); + } + if let Ok(lop) = input.try(|i| LengthOrPercentage::parse(context, i)) { + return Ok(OriginComponent::Length(lop)); + } + let keyword = S::parse(context, input)?; + Ok(OriginComponent::Side(keyword)) + } +} + +impl ToCss for OriginComponent + where S: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result + where W: fmt::Write, + { + match *self { + OriginComponent::Center => dest.write_str("center"), + OriginComponent::Length(ref lop) => lop.to_css(dest), + OriginComponent::Side(ref keyword) => keyword.to_css(dest), + } + } +} + +impl ToComputedValue for OriginComponent + where S: Side, +{ + type ComputedValue = ComputedLengthOrPercentage; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + OriginComponent::Center => { + ComputedLengthOrPercentage::Percentage(0.5) + }, + OriginComponent::Length(ref length) => { + length.to_computed_value(context) + }, + OriginComponent::Side(ref keyword) => { + let p = if keyword.is_start() { 0. } else { 1. }; + ComputedLengthOrPercentage::Percentage(p) + }, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + OriginComponent::Length(ToComputedValue::from_computed_value(computed)) + } +} diff --git a/tests/unit/style/parsing/effects.rs b/tests/unit/style/parsing/effects.rs index 9fcd03c5235..79fd1937e52 100644 --- a/tests/unit/style/parsing/effects.rs +++ b/tests/unit/style/parsing/effects.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use parsing::parse; -use style::properties::longhands::{self, perspective_origin, transform_origin}; +use style::properties::longhands::{perspective_origin, transform_origin}; use style_traits::ToCss; #[test] @@ -30,16 +30,6 @@ fn test_clip() { "rect(auto, auto, auto, auto)"); } -#[test] -fn test_longhands_parse_origin() { - assert_parser_exhausted!(longhands::parse_origin, "1px some-rubbish", false); - assert_parser_exhausted!(longhands::parse_origin, "1px 2px", true); - assert_parser_exhausted!(longhands::parse_origin, "center left", true); - assert_parser_exhausted!(longhands::parse_origin, "center right", true); - assert_parser_exhausted!(longhands::parse_origin, "center right 1px", true); - assert_parser_exhausted!(longhands::parse_origin, "1% right", false); -} - #[test] fn test_effects_parser_exhaustion() { assert_parser_exhausted!(perspective_origin::parse, "1px 1px", true); diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 578db7bc716..5e9cbc27ca5 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -25454,7 +25454,7 @@ "testharness" ], "mozilla/calc.html": [ - "47507adabc0d3642154b3ed4b1ab64d726fa682d", + "a1f441f7fd4b9ab3ae9e1cb5403cfe8de90bb71c", "testharness" ], "mozilla/canvas.initial.reset.2dstate.html": [ diff --git a/tests/wpt/mozilla/tests/mozilla/calc.html b/tests/wpt/mozilla/tests/mozilla/calc.html index d6a1a1a53cf..d3acc867b38 100644 --- a/tests/wpt/mozilla/tests/mozilla/calc.html +++ b/tests/wpt/mozilla/tests/mozilla/calc.html @@ -141,7 +141,7 @@ numberProperties.forEach(function(prop) { var otherProperties = [ ['border-width', 'calc(1px)', 'calc(1px)'], ['border-spacing', 'calc(1px)', 'calc(1px)'], - ['transform-origin', 'calc(1px + 0%)', 'calc(1px + 0%) 50% 0px'], + ['transform-origin', 'calc(1px + 0%)', 'calc(1px + 0%) center 0px'], ['perspective-origin', 'calc(1px + 0%)', 'calc(1px + 0%) center'], ['background-size', 'calc(1px + 0%)', 'calc(1px + 0%) auto'], ['background-position', 'calc(1px + 0%) calc(2px + 0%)', 'calc(1px + 0%) calc(2px + 0%)'],