stylo: support transform

This commit is contained in:
Manish Goregaokar 2016-11-10 12:00:55 -08:00
parent 86a5682247
commit bb5f351f4a
12 changed files with 2238 additions and 542 deletions

View file

@ -11,6 +11,7 @@
use app_units::Au;
use custom_properties::ComputedValuesMap;
use gecko_bindings::bindings;
% for style_struct in data.style_structs:
use gecko_bindings::structs::${style_struct.gecko_ffi_name};
use gecko_bindings::bindings::Gecko_Construct_${style_struct.gecko_ffi_name};
@ -961,7 +962,7 @@ fn static_assert() {
<% skip_box_longhands= """display overflow-y vertical-align
-moz-binding page-break-before page-break-after
scroll-snap-points-x scroll-snap-points-y""" %>
scroll-snap-points-x scroll-snap-points-y transform""" %>
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
// We manually-implement the |display| property until we get general
@ -1107,6 +1108,102 @@ fn static_assert() {
${impl_coord_copy('scroll_snap_points_y', 'mScrollSnapPointsY')}
<%def name="transform_function_arm(name, keyword, items)">
<%
pattern = None
if name == "matrix":
# m11, m12, m13, ..
indices = [str(i) + str(j) for i in range(1, 5) for j in range(1, 5)]
# m11: number1, m12: number2, ..
single_patterns = ["m%s: number%s" % (index, i + 1) for (i, index) in enumerate(indices)]
pattern = "ComputedMatrix { %s }" % ", ".join(single_patterns)
else:
# Generate contents of pattern from items
pattern = ", ".join([b + str(a+1) for (a,b) in enumerate(items)])
# First %s substituted with the call to GetArrayItem, the second
# %s substituted with the corresponding variable
css_value_setters = {
"length" : "bindings::Gecko_CSSValue_SetAbsoluteLength(%s, %s.0)",
"percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s)",
"lop" : "set_lop(%s, %s)",
"angle" : "bindings::Gecko_CSSValue_SetAngle(%s, %s.0)",
"number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)",
}
%>
ComputedOperation::${name.title()}(${pattern}) => {
bindings::Gecko_CSSValue_SetFunction(gecko_value, ${len(items) + 1});
bindings::Gecko_CSSValue_SetKeyword(
bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0),
eCSSKeyword_${keyword}
);
% for index, item in enumerate(items):
${css_value_setters[item] % (
"bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d)" % (index + 1),
item + str(index + 1)
)};
% endfor
}
</%def>
pub fn set_transform(&mut self, other: longhands::transform::computed_value::T) {
use gecko_bindings::structs::nsCSSKeyword::*;
use gecko_bindings::sugar::refptr::RefPtr;
use properties::longhands::transform::computed_value::ComputedMatrix;
use properties::longhands::transform::computed_value::ComputedOperation;
use values::computed::LengthOrPercentage;
unsafe fn set_lop(value: &mut structs::nsCSSValue, lop: LengthOrPercentage) {
match lop {
LengthOrPercentage::Length(au) => {
bindings::Gecko_CSSValue_SetAbsoluteLength(value, au.0)
}
LengthOrPercentage::Percentage(pc) => {
bindings::Gecko_CSSValue_SetPercentage(value, pc)
}
LengthOrPercentage::Calc(calc) => {
bindings::Gecko_CSSValue_SetCalc(value, calc.into())
}
}
}
let vec = if let Some(v) = other.0 {
v
} else {
unsafe {
self.gecko.mSpecifiedTransform.clear();
}
return;
};
let list = unsafe {
RefPtr::from_addrefed(bindings::Gecko_NewCSSValueSharedList(vec.len() as u32))
};
let mut cur = list.mHead;
let mut iter = vec.into_iter();
while !cur.is_null() {
let gecko_value = unsafe { &mut (*cur).mValue };
let servo = iter.next().expect("Gecko_NewCSSValueSharedList should create a shared \
value list of the same length as the transform vector");
unsafe {
match servo {
${transform_function_arm("matrix", "matrix3d", ["number"] * 16)}
${transform_function_arm("skew", "skew", ["angle"] * 2)}
${transform_function_arm("translate", "translate3d", ["lop", "lop", "length"])}
${transform_function_arm("scale", "scale3d", ["number"] * 3)}
${transform_function_arm("rotate", "rotate3d", ["number"] * 3 + ["angle"])}
${transform_function_arm("perspective", "perspective", ["length"])}
}
cur = (*cur).mNext;
}
}
debug_assert!(iter.next().is_none());
unsafe { self.gecko.mSpecifiedTransform.set_move(list) };
}
pub fn copy_transform_from(&mut self, other: &Self) {
unsafe { self.gecko.mSpecifiedTransform.set(&other.gecko.mSpecifiedTransform); }
}
</%self:impl_trait>
<%def name="simple_image_array_property(name, shorthand, field_name)">
@ -1609,7 +1706,6 @@ fn static_assert() {
Gecko_CopyFiltersFrom(&other.gecko as *const _ as *mut _, &mut self.gecko);
}
}
</%self:impl_trait>

View file

@ -937,6 +937,528 @@ ${helpers.keyword_list("animation-fill-mode",
</%helpers:longhand>
<%helpers:longhand name="transform" products="gecko servo" animatable="${product == 'servo'}">
use app_units::Au;
use style_traits::ToCss;
use values::CSSFloat;
use values::HasViewportPercentage;
use std::fmt;
pub mod computed_value {
use values::CSSFloat;
use values::computed;
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct ComputedMatrix {
pub m11: CSSFloat, pub m12: CSSFloat, pub m13: CSSFloat, pub m14: CSSFloat,
pub m21: CSSFloat, pub m22: CSSFloat, pub m23: CSSFloat, pub m24: CSSFloat,
pub m31: CSSFloat, pub m32: CSSFloat, pub m33: CSSFloat, pub m34: CSSFloat,
pub m41: CSSFloat, pub m42: CSSFloat, pub m43: CSSFloat, pub m44: CSSFloat,
}
impl ComputedMatrix {
pub fn identity() -> ComputedMatrix {
ComputedMatrix {
m11: 1.0, m12: 0.0, m13: 0.0, m14: 0.0,
m21: 0.0, m22: 1.0, m23: 0.0, m24: 0.0,
m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
m41: 0.0, m42: 0.0, m43: 0.0, m44: 1.0
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum ComputedOperation {
Matrix(ComputedMatrix),
Skew(computed::Angle, computed::Angle),
Translate(computed::LengthOrPercentage,
computed::LengthOrPercentage,
computed::Length),
Scale(CSSFloat, CSSFloat, CSSFloat),
Rotate(CSSFloat, CSSFloat, CSSFloat, computed::Angle),
Perspective(computed::Length),
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct T(pub Option<Vec<ComputedOperation>>);
}
pub use self::computed_value::ComputedMatrix as SpecifiedMatrix;
fn parse_two_lengths_or_percentages(input: &mut Parser)
-> Result<(specified::LengthOrPercentage,
specified::LengthOrPercentage),()> {
let first = try!(specified::LengthOrPercentage::parse(input));
let second = input.try(|input| {
try!(input.expect_comma());
specified::LengthOrPercentage::parse(input)
}).unwrap_or(specified::LengthOrPercentage::zero());
Ok((first, second))
}
fn parse_two_floats(input: &mut Parser) -> Result<(CSSFloat,CSSFloat),()> {
let first = try!(specified::parse_number(input));
let second = input.try(|input| {
try!(input.expect_comma());
specified::parse_number(input)
}).unwrap_or(first);
Ok((first, second))
}
fn parse_two_angles(input: &mut Parser) -> Result<(specified::Angle, specified::Angle),()> {
let first = try!(specified::Angle::parse(input));
let second = input.try(|input| {
try!(input.expect_comma());
specified::Angle::parse(input)
}).unwrap_or(specified::Angle(0.0));
Ok((first, second))
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
enum TranslateKind {
Translate,
TranslateX,
TranslateY,
TranslateZ,
Translate3D,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
enum SpecifiedOperation {
Matrix(SpecifiedMatrix),
Skew(specified::Angle, specified::Angle),
Translate(TranslateKind,
specified::LengthOrPercentage,
specified::LengthOrPercentage,
specified::Length),
Scale(CSSFloat, CSSFloat, CSSFloat),
Rotate(CSSFloat, CSSFloat, CSSFloat, specified::Angle),
Perspective(specified::Length),
}
impl ToCss for computed_value::T {
fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
// TODO(pcwalton)
Ok(())
}
}
impl HasViewportPercentage for SpecifiedOperation {
fn has_viewport_percentage(&self) -> bool {
match *self {
SpecifiedOperation::Translate(_, l1, l2, l3) => {
l1.has_viewport_percentage() ||
l2.has_viewport_percentage() ||
l3.has_viewport_percentage()
},
SpecifiedOperation::Perspective(length) => length.has_viewport_percentage(),
_ => false
}
}
}
impl ToCss for SpecifiedOperation {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
// todo(gw): implement serialization for transform
// types other than translate.
SpecifiedOperation::Matrix(_m) => {
Ok(())
}
SpecifiedOperation::Skew(_sx, _sy) => {
Ok(())
}
SpecifiedOperation::Translate(kind, tx, ty, tz) => {
match kind {
TranslateKind::Translate => {
try!(dest.write_str("translate("));
try!(tx.to_css(dest));
try!(dest.write_str(", "));
try!(ty.to_css(dest));
dest.write_str(")")
}
TranslateKind::TranslateX => {
try!(dest.write_str("translateX("));
try!(tx.to_css(dest));
dest.write_str(")")
}
TranslateKind::TranslateY => {
try!(dest.write_str("translateY("));
try!(ty.to_css(dest));
dest.write_str(")")
}
TranslateKind::TranslateZ => {
try!(dest.write_str("translateZ("));
try!(tz.to_css(dest));
dest.write_str(")")
}
TranslateKind::Translate3D => {
try!(dest.write_str("translate3d("));
try!(tx.to_css(dest));
try!(dest.write_str(", "));
try!(ty.to_css(dest));
try!(dest.write_str(", "));
try!(tz.to_css(dest));
dest.write_str(")")
}
}
}
SpecifiedOperation::Scale(_sx, _sy, _sz) => {
Ok(())
}
SpecifiedOperation::Rotate(_ax, _ay, _az, _angle) => {
Ok(())
}
SpecifiedOperation::Perspective(_p) => {
Ok(())
}
}
}
}
impl HasViewportPercentage for SpecifiedValue {
fn has_viewport_percentage(&self) -> bool {
let &SpecifiedValue(ref specified_ops) = self;
specified_ops.iter().any(|ref x| x.has_viewport_percentage())
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct SpecifiedValue(Vec<SpecifiedOperation>);
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut first = true;
for operation in &self.0 {
if !first {
try!(dest.write_str(" "));
}
first = false;
try!(operation.to_css(dest))
}
Ok(())
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(None)
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(SpecifiedValue(Vec::new()))
}
let mut result = Vec::new();
loop {
let name = match input.expect_function() {
Ok(name) => name,
Err(_) => break,
};
match_ignore_ascii_case! {
name,
"matrix" => {
try!(input.parse_nested_block(|input| {
let values = try!(input.parse_comma_separated(|input| {
specified::parse_number(input)
}));
if values.len() != 6 {
return Err(())
}
result.push(SpecifiedOperation::Matrix(
SpecifiedMatrix {
m11: values[0], m12: values[1], m13: 0.0, m14: 0.0,
m21: values[2], m22: values[3], m23: 0.0, m24: 0.0,
m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
m41: values[4], m42: values[5], m43: 0.0, m44: 1.0
}));
Ok(())
}))
},
"matrix3d" => {
try!(input.parse_nested_block(|input| {
let values = try!(input.parse_comma_separated(|input| {
specified::parse_number(input)
}));
if values.len() != 16 {
return Err(())
}
result.push(SpecifiedOperation::Matrix(
SpecifiedMatrix {
m11: values[ 0], m12: values[ 1], m13: values[ 2], m14: values[ 3],
m21: values[ 4], m22: values[ 5], m23: values[ 6], m24: values[ 7],
m31: values[ 8], m32: values[ 9], m33: values[10], m34: values[11],
m41: values[12], m42: values[13], m43: values[14], m44: values[15]
}));
Ok(())
}))
},
"translate" => {
try!(input.parse_nested_block(|input| {
let (tx, ty) = try!(parse_two_lengths_or_percentages(input));
result.push(SpecifiedOperation::Translate(TranslateKind::Translate,
tx,
ty,
specified::Length::Absolute(Au(0))));
Ok(())
}))
},
"translatex" => {
try!(input.parse_nested_block(|input| {
let tx = try!(specified::LengthOrPercentage::parse(input));
result.push(SpecifiedOperation::Translate(
TranslateKind::TranslateX,
tx,
specified::LengthOrPercentage::zero(),
specified::Length::Absolute(Au(0))));
Ok(())
}))
},
"translatey" => {
try!(input.parse_nested_block(|input| {
let ty = try!(specified::LengthOrPercentage::parse(input));
result.push(SpecifiedOperation::Translate(
TranslateKind::TranslateY,
specified::LengthOrPercentage::zero(),
ty,
specified::Length::Absolute(Au(0))));
Ok(())
}))
},
"translatez" => {
try!(input.parse_nested_block(|input| {
let tz = try!(specified::Length::parse(input));
result.push(SpecifiedOperation::Translate(
TranslateKind::TranslateZ,
specified::LengthOrPercentage::zero(),
specified::LengthOrPercentage::zero(),
tz));
Ok(())
}))
},
"translate3d" => {
try!(input.parse_nested_block(|input| {
let tx = try!(specified::LengthOrPercentage::parse(input));
try!(input.expect_comma());
let ty = try!(specified::LengthOrPercentage::parse(input));
try!(input.expect_comma());
let tz = try!(specified::Length::parse(input));
result.push(SpecifiedOperation::Translate(
TranslateKind::Translate3D,
tx,
ty,
tz));
Ok(())
}))
},
"scale" => {
try!(input.parse_nested_block(|input| {
let (sx, sy) = try!(parse_two_floats(input));
result.push(SpecifiedOperation::Scale(sx, sy, 1.0));
Ok(())
}))
},
"scalex" => {
try!(input.parse_nested_block(|input| {
let sx = try!(specified::parse_number(input));
result.push(SpecifiedOperation::Scale(sx, 1.0, 1.0));
Ok(())
}))
},
"scaley" => {
try!(input.parse_nested_block(|input| {
let sy = try!(specified::parse_number(input));
result.push(SpecifiedOperation::Scale(1.0, sy, 1.0));
Ok(())
}))
},
"scalez" => {
try!(input.parse_nested_block(|input| {
let sz = try!(specified::parse_number(input));
result.push(SpecifiedOperation::Scale(1.0, 1.0, sz));
Ok(())
}))
},
"scale3d" => {
try!(input.parse_nested_block(|input| {
let sx = try!(specified::parse_number(input));
try!(input.expect_comma());
let sy = try!(specified::parse_number(input));
try!(input.expect_comma());
let sz = try!(specified::parse_number(input));
result.push(SpecifiedOperation::Scale(sx, sy, sz));
Ok(())
}))
},
"rotate" => {
try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta));
Ok(())
}))
},
"rotatex" => {
try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(1.0, 0.0, 0.0, theta));
Ok(())
}))
},
"rotatey" => {
try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(0.0, 1.0, 0.0, theta));
Ok(())
}))
},
"rotatez" => {
try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta));
Ok(())
}))
},
"rotate3d" => {
try!(input.parse_nested_block(|input| {
let ax = try!(specified::parse_number(input));
try!(input.expect_comma());
let ay = try!(specified::parse_number(input));
try!(input.expect_comma());
let az = try!(specified::parse_number(input));
try!(input.expect_comma());
let theta = try!(specified::Angle::parse(input));
// TODO(gw): Check the axis can be normalized!!
result.push(SpecifiedOperation::Rotate(ax, ay, az, theta));
Ok(())
}))
},
"skew" => {
try!(input.parse_nested_block(|input| {
let (theta_x, theta_y) = try!(parse_two_angles(input));
result.push(SpecifiedOperation::Skew(theta_x, theta_y));
Ok(())
}))
},
"skewx" => {
try!(input.parse_nested_block(|input| {
let theta_x = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Skew(theta_x, specified::Angle(0.0)));
Ok(())
}))
},
"skewy" => {
try!(input.parse_nested_block(|input| {
let theta_y = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Skew(specified::Angle(0.0), theta_y));
Ok(())
}))
},
"perspective" => {
try!(input.parse_nested_block(|input| {
let d = try!(specified::Length::parse(input));
result.push(SpecifiedOperation::Perspective(d));
Ok(())
}))
},
_ => return Err(())
}
}
if !result.is_empty() {
Ok(SpecifiedValue(result))
} else {
Err(())
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
if self.0.is_empty() {
return computed_value::T(None)
}
let mut result = vec!();
for operation in &self.0 {
match *operation {
SpecifiedOperation::Matrix(ref matrix) => {
result.push(computed_value::ComputedOperation::Matrix(*matrix));
}
SpecifiedOperation::Translate(_, ref tx, ref ty, ref tz) => {
result.push(computed_value::ComputedOperation::Translate(tx.to_computed_value(context),
ty.to_computed_value(context),
tz.to_computed_value(context)));
}
SpecifiedOperation::Scale(sx, sy, sz) => {
result.push(computed_value::ComputedOperation::Scale(sx, sy, sz));
}
SpecifiedOperation::Rotate(ax, ay, az, theta) => {
let len = (ax * ax + ay * ay + az * az).sqrt();
result.push(computed_value::ComputedOperation::Rotate(ax / len, ay / len, az / len, theta));
}
SpecifiedOperation::Skew(theta_x, theta_y) => {
result.push(computed_value::ComputedOperation::Skew(theta_x, theta_y));
}
SpecifiedOperation::Perspective(d) => {
result.push(computed_value::ComputedOperation::Perspective(d.to_computed_value(context)));
}
};
}
computed_value::T(Some(result))
}
#[inline]
fn from_computed_value(computed: &computed_value::T) -> Self {
SpecifiedValue(computed.0.as_ref().map(|computed| {
let mut result = vec!();
for operation in computed {
match *operation {
computed_value::ComputedOperation::Matrix(ref matrix) => {
result.push(SpecifiedOperation::Matrix(*matrix));
}
computed_value::ComputedOperation::Translate(ref tx, ref ty, ref tz) => {
// XXXManishearth we lose information here; perhaps we should try to
// recover the original function? Not sure if this can be observed.
result.push(SpecifiedOperation::Translate(TranslateKind::Translate,
ToComputedValue::from_computed_value(tx),
ToComputedValue::from_computed_value(ty),
ToComputedValue::from_computed_value(tz)));
}
computed_value::ComputedOperation::Scale(sx, sy, sz) => {
result.push(SpecifiedOperation::Scale(sx, sy, sz));
}
computed_value::ComputedOperation::Rotate(ax, ay, az, theta) => {
result.push(SpecifiedOperation::Rotate(ax, ay, az, theta));
}
computed_value::ComputedOperation::Skew(theta_x, theta_y) => {
result.push(SpecifiedOperation::Skew(theta_x, theta_y));
}
computed_value::ComputedOperation::Perspective(ref d) => {
result.push(SpecifiedOperation::Perspective(
ToComputedValue::from_computed_value(d)
));
}
};
}
result
}).unwrap_or(Vec::new()))
}
}
</%helpers:longhand>
// CSSOM View Module
// https://www.w3.org/TR/cssom-view-1/
${helpers.single_keyword("scroll-behavior",

View file

@ -670,525 +670,6 @@ ${helpers.predefined_type("opacity",
}
</%helpers:longhand>
<%helpers:longhand name="transform" products="servo" animatable="True">
use app_units::Au;
use std::fmt;
use style_traits::ToCss;
use values::{CSSFloat, HasViewportPercentage};
pub mod computed_value {
use values::CSSFloat;
use values::computed;
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct ComputedMatrix {
pub m11: CSSFloat, pub m12: CSSFloat, pub m13: CSSFloat, pub m14: CSSFloat,
pub m21: CSSFloat, pub m22: CSSFloat, pub m23: CSSFloat, pub m24: CSSFloat,
pub m31: CSSFloat, pub m32: CSSFloat, pub m33: CSSFloat, pub m34: CSSFloat,
pub m41: CSSFloat, pub m42: CSSFloat, pub m43: CSSFloat, pub m44: CSSFloat,
}
impl ComputedMatrix {
pub fn identity() -> ComputedMatrix {
ComputedMatrix {
m11: 1.0, m12: 0.0, m13: 0.0, m14: 0.0,
m21: 0.0, m22: 1.0, m23: 0.0, m24: 0.0,
m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
m41: 0.0, m42: 0.0, m43: 0.0, m44: 1.0
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum ComputedOperation {
Matrix(ComputedMatrix),
Skew(computed::Angle, computed::Angle),
Translate(computed::LengthOrPercentage,
computed::LengthOrPercentage,
computed::Length),
Scale(CSSFloat, CSSFloat, CSSFloat),
Rotate(CSSFloat, CSSFloat, CSSFloat, computed::Angle),
Perspective(computed::Length),
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct T(pub Option<Vec<ComputedOperation>>);
}
pub use self::computed_value::ComputedMatrix as SpecifiedMatrix;
fn parse_two_lengths_or_percentages(input: &mut Parser)
-> Result<(specified::LengthOrPercentage,
specified::LengthOrPercentage),()> {
let first = try!(specified::LengthOrPercentage::parse(input));
let second = input.try(|input| {
try!(input.expect_comma());
specified::LengthOrPercentage::parse(input)
}).unwrap_or(specified::LengthOrPercentage::zero());
Ok((first, second))
}
fn parse_two_floats(input: &mut Parser) -> Result<(CSSFloat,CSSFloat),()> {
let first = try!(specified::parse_number(input));
let second = input.try(|input| {
try!(input.expect_comma());
specified::parse_number(input)
}).unwrap_or(first);
Ok((first, second))
}
fn parse_two_angles(input: &mut Parser) -> Result<(specified::Angle, specified::Angle),()> {
let first = try!(specified::Angle::parse(input));
let second = input.try(|input| {
try!(input.expect_comma());
specified::Angle::parse(input)
}).unwrap_or(specified::Angle(0.0));
Ok((first, second))
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
enum TranslateKind {
Translate,
TranslateX,
TranslateY,
TranslateZ,
Translate3D,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
enum SpecifiedOperation {
Matrix(SpecifiedMatrix),
Skew(specified::Angle, specified::Angle),
Translate(TranslateKind,
specified::LengthOrPercentage,
specified::LengthOrPercentage,
specified::Length),
Scale(CSSFloat, CSSFloat, CSSFloat),
Rotate(CSSFloat, CSSFloat, CSSFloat, specified::Angle),
Perspective(specified::Length),
}
impl ToCss for computed_value::T {
fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
// TODO(pcwalton)
Ok(())
}
}
impl HasViewportPercentage for SpecifiedOperation {
fn has_viewport_percentage(&self) -> bool {
match *self {
SpecifiedOperation::Translate(_, l1, l2, l3) => {
l1.has_viewport_percentage() ||
l2.has_viewport_percentage() ||
l3.has_viewport_percentage()
},
SpecifiedOperation::Perspective(length) => length.has_viewport_percentage(),
_ => false
}
}
}
impl ToCss for SpecifiedOperation {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
// todo(gw): implement serialization for transform
// types other than translate.
SpecifiedOperation::Matrix(_m) => {
Ok(())
}
SpecifiedOperation::Skew(_sx, _sy) => {
Ok(())
}
SpecifiedOperation::Translate(kind, tx, ty, tz) => {
match kind {
TranslateKind::Translate => {
try!(dest.write_str("translate("));
try!(tx.to_css(dest));
try!(dest.write_str(", "));
try!(ty.to_css(dest));
dest.write_str(")")
}
TranslateKind::TranslateX => {
try!(dest.write_str("translateX("));
try!(tx.to_css(dest));
dest.write_str(")")
}
TranslateKind::TranslateY => {
try!(dest.write_str("translateY("));
try!(ty.to_css(dest));
dest.write_str(")")
}
TranslateKind::TranslateZ => {
try!(dest.write_str("translateZ("));
try!(tz.to_css(dest));
dest.write_str(")")
}
TranslateKind::Translate3D => {
try!(dest.write_str("translate3d("));
try!(tx.to_css(dest));
try!(dest.write_str(", "));
try!(ty.to_css(dest));
try!(dest.write_str(", "));
try!(tz.to_css(dest));
dest.write_str(")")
}
}
}
SpecifiedOperation::Scale(_sx, _sy, _sz) => {
Ok(())
}
SpecifiedOperation::Rotate(_ax, _ay, _az, _angle) => {
Ok(())
}
SpecifiedOperation::Perspective(_p) => {
Ok(())
}
}
}
}
impl HasViewportPercentage for SpecifiedValue {
fn has_viewport_percentage(&self) -> bool {
let &SpecifiedValue(ref specified_ops) = self;
specified_ops.iter().any(|ref x| x.has_viewport_percentage())
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct SpecifiedValue(Vec<SpecifiedOperation>);
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut first = true;
for operation in &self.0 {
if !first {
try!(dest.write_str(" "));
}
first = false;
try!(operation.to_css(dest))
}
Ok(())
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(None)
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(SpecifiedValue(Vec::new()))
}
let mut result = Vec::new();
loop {
let name = match input.expect_function() {
Ok(name) => name,
Err(_) => break,
};
match_ignore_ascii_case! {
name,
"matrix" => {
try!(input.parse_nested_block(|input| {
let values = try!(input.parse_comma_separated(|input| {
specified::parse_number(input)
}));
if values.len() != 6 {
return Err(())
}
result.push(SpecifiedOperation::Matrix(
SpecifiedMatrix {
m11: values[0], m12: values[1], m13: 0.0, m14: 0.0,
m21: values[2], m22: values[3], m23: 0.0, m24: 0.0,
m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
m41: values[4], m42: values[5], m43: 0.0, m44: 1.0
}));
Ok(())
}))
},
"matrix3d" => {
try!(input.parse_nested_block(|input| {
let values = try!(input.parse_comma_separated(|input| {
specified::parse_number(input)
}));
if values.len() != 16 {
return Err(())
}
result.push(SpecifiedOperation::Matrix(
SpecifiedMatrix {
m11: values[ 0], m12: values[ 1], m13: values[ 2], m14: values[ 3],
m21: values[ 4], m22: values[ 5], m23: values[ 6], m24: values[ 7],
m31: values[ 8], m32: values[ 9], m33: values[10], m34: values[11],
m41: values[12], m42: values[13], m43: values[14], m44: values[15]
}));
Ok(())
}))
},
"translate" => {
try!(input.parse_nested_block(|input| {
let (tx, ty) = try!(parse_two_lengths_or_percentages(input));
result.push(SpecifiedOperation::Translate(TranslateKind::Translate,
tx,
ty,
specified::Length::Absolute(Au(0))));
Ok(())
}))
},
"translatex" => {
try!(input.parse_nested_block(|input| {
let tx = try!(specified::LengthOrPercentage::parse(input));
result.push(SpecifiedOperation::Translate(
TranslateKind::TranslateX,
tx,
specified::LengthOrPercentage::zero(),
specified::Length::Absolute(Au(0))));
Ok(())
}))
},
"translatey" => {
try!(input.parse_nested_block(|input| {
let ty = try!(specified::LengthOrPercentage::parse(input));
result.push(SpecifiedOperation::Translate(
TranslateKind::TranslateY,
specified::LengthOrPercentage::zero(),
ty,
specified::Length::Absolute(Au(0))));
Ok(())
}))
},
"translatez" => {
try!(input.parse_nested_block(|input| {
let tz = try!(specified::Length::parse(input));
result.push(SpecifiedOperation::Translate(
TranslateKind::TranslateZ,
specified::LengthOrPercentage::zero(),
specified::LengthOrPercentage::zero(),
tz));
Ok(())
}))
},
"translate3d" => {
try!(input.parse_nested_block(|input| {
let tx = try!(specified::LengthOrPercentage::parse(input));
try!(input.expect_comma());
let ty = try!(specified::LengthOrPercentage::parse(input));
try!(input.expect_comma());
let tz = try!(specified::Length::parse(input));
result.push(SpecifiedOperation::Translate(
TranslateKind::Translate3D,
tx,
ty,
tz));
Ok(())
}))
},
"scale" => {
try!(input.parse_nested_block(|input| {
let (sx, sy) = try!(parse_two_floats(input));
result.push(SpecifiedOperation::Scale(sx, sy, 1.0));
Ok(())
}))
},
"scalex" => {
try!(input.parse_nested_block(|input| {
let sx = try!(specified::parse_number(input));
result.push(SpecifiedOperation::Scale(sx, 1.0, 1.0));
Ok(())
}))
},
"scaley" => {
try!(input.parse_nested_block(|input| {
let sy = try!(specified::parse_number(input));
result.push(SpecifiedOperation::Scale(1.0, sy, 1.0));
Ok(())
}))
},
"scalez" => {
try!(input.parse_nested_block(|input| {
let sz = try!(specified::parse_number(input));
result.push(SpecifiedOperation::Scale(1.0, 1.0, sz));
Ok(())
}))
},
"scale3d" => {
try!(input.parse_nested_block(|input| {
let sx = try!(specified::parse_number(input));
try!(input.expect_comma());
let sy = try!(specified::parse_number(input));
try!(input.expect_comma());
let sz = try!(specified::parse_number(input));
result.push(SpecifiedOperation::Scale(sx, sy, sz));
Ok(())
}))
},
"rotate" => {
try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta));
Ok(())
}))
},
"rotatex" => {
try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(1.0, 0.0, 0.0, theta));
Ok(())
}))
},
"rotatey" => {
try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(0.0, 1.0, 0.0, theta));
Ok(())
}))
},
"rotatez" => {
try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta));
Ok(())
}))
},
"rotate3d" => {
try!(input.parse_nested_block(|input| {
let ax = try!(specified::parse_number(input));
try!(input.expect_comma());
let ay = try!(specified::parse_number(input));
try!(input.expect_comma());
let az = try!(specified::parse_number(input));
try!(input.expect_comma());
let theta = try!(specified::Angle::parse(input));
// TODO(gw): Check the axis can be normalized!!
result.push(SpecifiedOperation::Rotate(ax, ay, az, theta));
Ok(())
}))
},
"skew" => {
try!(input.parse_nested_block(|input| {
let (theta_x, theta_y) = try!(parse_two_angles(input));
result.push(SpecifiedOperation::Skew(theta_x, theta_y));
Ok(())
}))
},
"skewx" => {
try!(input.parse_nested_block(|input| {
let theta_x = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Skew(theta_x, specified::Angle(0.0)));
Ok(())
}))
},
"skewy" => {
try!(input.parse_nested_block(|input| {
let theta_y = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Skew(specified::Angle(0.0), theta_y));
Ok(())
}))
},
"perspective" => {
try!(input.parse_nested_block(|input| {
let d = try!(specified::Length::parse(input));
result.push(SpecifiedOperation::Perspective(d));
Ok(())
}))
},
_ => return Err(())
}
}
if !result.is_empty() {
Ok(SpecifiedValue(result))
} else {
Err(())
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
if self.0.is_empty() {
return computed_value::T(None)
}
let mut result = vec!();
for operation in &self.0 {
match *operation {
SpecifiedOperation::Matrix(ref matrix) => {
result.push(computed_value::ComputedOperation::Matrix(*matrix));
}
SpecifiedOperation::Translate(_, ref tx, ref ty, ref tz) => {
result.push(computed_value::ComputedOperation::Translate(tx.to_computed_value(context),
ty.to_computed_value(context),
tz.to_computed_value(context)));
}
SpecifiedOperation::Scale(sx, sy, sz) => {
result.push(computed_value::ComputedOperation::Scale(sx, sy, sz));
}
SpecifiedOperation::Rotate(ax, ay, az, theta) => {
let len = (ax * ax + ay * ay + az * az).sqrt();
result.push(computed_value::ComputedOperation::Rotate(ax / len, ay / len, az / len, theta));
}
SpecifiedOperation::Skew(theta_x, theta_y) => {
result.push(computed_value::ComputedOperation::Skew(theta_x, theta_y));
}
SpecifiedOperation::Perspective(d) => {
result.push(computed_value::ComputedOperation::Perspective(d.to_computed_value(context)));
}
};
}
computed_value::T(Some(result))
}
#[inline]
fn from_computed_value(computed: &computed_value::T) -> Self {
SpecifiedValue(computed.0.as_ref().map(|computed| {
let mut result = vec!();
for operation in computed {
match *operation {
computed_value::ComputedOperation::Matrix(ref matrix) => {
result.push(SpecifiedOperation::Matrix(*matrix));
}
computed_value::ComputedOperation::Translate(ref tx, ref ty, ref tz) => {
// XXXManishearth we lose information here; perhaps we should try to
// recover the original function? Not sure if this can be observed.
result.push(SpecifiedOperation::Translate(TranslateKind::Translate,
ToComputedValue::from_computed_value(tx),
ToComputedValue::from_computed_value(ty),
ToComputedValue::from_computed_value(tz)));
}
computed_value::ComputedOperation::Scale(sx, sy, sz) => {
result.push(SpecifiedOperation::Scale(sx, sy, sz));
}
computed_value::ComputedOperation::Rotate(ax, ay, az, theta) => {
result.push(SpecifiedOperation::Rotate(ax, ay, az, theta));
}
computed_value::ComputedOperation::Skew(theta_x, theta_y) => {
result.push(SpecifiedOperation::Skew(theta_x, theta_y));
}
computed_value::ComputedOperation::Perspective(ref d) => {
result.push(SpecifiedOperation::Perspective(
ToComputedValue::from_computed_value(d)
));
}
};
}
result
}).unwrap_or(Vec::new()))
}
}
</%helpers:longhand>
pub struct OriginParseResult {
pub horizontal: Option<specified::LengthOrPercentage>,
pub vertical: Option<specified::LengthOrPercentage>,

View file

@ -1237,6 +1237,7 @@ impl ComputedValues {
use computed_values::transform_style;
let effects = self.get_effects();
let box_ = self.get_box();
// TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.
if effects.opacity < 1.0 ||
@ -1247,7 +1248,7 @@ impl ComputedValues {
}
if effects.transform_style == transform_style::T::auto {
if effects.transform.0.is_some() {
if box_.transform.0.is_some() {
return transform_style::T::flat;
}
if let Either::First(ref _length) = effects.perspective {
@ -1261,7 +1262,7 @@ impl ComputedValues {
pub fn transform_requires_layer(&self) -> bool {
// Check if the transform matrix is 2D or 3D
if let Some(ref transform_list) = self.get_effects().transform.0 {
if let Some(ref transform_list) = self.get_box().transform.0 {
for transform in transform_list {
match *transform {
computed_values::transform::ComputedOperation::Perspective(..) => {