mirror of
https://github.com/servo/servo.git
synced 2025-08-15 10:25:32 +01:00
stylo: support transform
This commit is contained in:
parent
86a5682247
commit
bb5f351f4a
12 changed files with 2238 additions and 542 deletions
|
@ -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>
|
||||
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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(..) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue