Make use of generics for transform-origin

This allows us to preserve keywords during serialisation.
This commit is contained in:
Anthony Ramine 2017-05-30 15:38:26 +02:00
parent b42aaf28df
commit 5d70580813
12 changed files with 241 additions and 221 deletions

View file

@ -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<Self, ()> {
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<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
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<W>(&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<W>(&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<SpecifiedValue,()> {
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:longhand>
${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`,

View file

@ -429,96 +429,6 @@ ${helpers.predefined_type("clip",
}
</%helpers:longhand>
pub struct OriginParseResult {
pub horizontal: Option<specified::LengthOrPercentage>,
pub vertical: Option<specified::LengthOrPercentage>,
pub depth: Option<specified::NoCalcLength>
}
pub fn parse_origin(context: &ParserContext, input: &mut Parser) -> Result<OriginParseResult,()> {
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

View file

@ -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" />

View file

@ -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.

View file

@ -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 `<transform-origin>`
pub type TransformOrigin = GenericTransformOrigin<LengthOrPercentage, LengthOrPercentage, Length>;
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<Self, ()> {
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<f64, ()> {
self.compute_squared_distance(other).map(f64::sqrt)
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(
self.horizontal.compute_squared_distance(&other.horizontal)? +
self.vertical.compute_squared_distance(&other.vertical)? +
self.depth.compute_squared_distance(&other.depth)?
)
}
}

View file

@ -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:

View file

@ -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<H, V, Depth> {
/// The horizontal origin.
pub horizontal: H,
/// The vertical origin.
pub vertical: V,
/// The depth.
pub depth: Depth,
}
impl<H, V, D> TransformOrigin<H, V, D> {
/// Returns a new transform origin.
pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
Self {
horizontal: horizontal,
vertical: vertical,
depth: depth,
}
}
}
impl<H, V, D> ToCss for TransformOrigin<H, V, D>
where H: ToCss, V: ToCss, D: ToCss,
{
fn to_css<W>(&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)
}
}

View file

@ -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 {

View file

@ -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 `<transform-origin>`
pub type TransformOrigin = GenericTransformOrigin<OriginComponent<X>, OriginComponent<Y>, Length>;
/// The specified value of a component of a CSS `<transform-origin>`.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
pub enum OriginComponent<S> {
/// `center`
Center,
/// `<lop>`
Length(LengthOrPercentage),
/// `<side>`
Side(S),
}
impl Parse for TransformOrigin {
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
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<S> Parse for OriginComponent<S>
where S: Parse,
{
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
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<S: ToCss> ToCss for OriginComponent<S>
where S: ToCss,
{
fn to_css<W>(&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<S> ToComputedValue for OriginComponent<S>
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))
}
}

View file

@ -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);

View file

@ -25454,7 +25454,7 @@
"testharness"
],
"mozilla/calc.html": [
"47507adabc0d3642154b3ed4b1ab64d726fa682d",
"a1f441f7fd4b9ab3ae9e1cb5403cfe8de90bb71c",
"testharness"
],
"mozilla/canvas.initial.reset.2dstate.html": [

View file

@ -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%)'],