From e4080bb4b41ca39a0865abc7b1a24bf04c9a2e8d Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Sun, 12 Nov 2017 13:04:22 -0600 Subject: [PATCH] servo: Merge #19141 - Implement -moz-window-transform{,-origin} internal properties (from upsuper:window-transform); r=emilio This fixes [bug 1374178](https://bugzilla.mozilla.org/show_bug.cgi?id=1374178). Note that the impl of `-moz-window-transform-origin` doesn't exactly match how it is implemented in Gecko. Specifically, that property doesn't accept depth value in Gecko, but it does in this impl. The depth value is simply dropped during conversion. This is because I don't think it's worth adding code for handling this internal property to make it as restrictive. This is a modified version of #19106. The only difference since then is how `impl_transform_origin` distinguish between `transform-origin` and `-moz-window-transform-origin`. The previous PR has a bug which checks against `transform` rather than `transform-origin`, while this PR changes it to checking the length of the array. It introduces an unsuppressible warning due to rust-lang/rust#45850. This doesn't cause build failure, so maybe it's fine? Source-Repo: https://github.com/servo/servo Source-Revision: bc58e18761ef687bd6c5efcda635cabdb742069a --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 28feb95c087a21b85d88d8c530d4754e45f18425 --- .../components/style/properties/gecko.mako.rs | 754 +++++++++--------- .../style/properties/longhand/box.mako.rs | 44 +- .../style/properties/longhand/ui.mako.rs | 17 + .../style/properties/shorthand/box.mako.rs | 5 +- servo/components/style/values/computed/mod.rs | 2 +- .../style/values/generics/transform.rs | 7 + .../components/style/values/specified/mod.rs | 2 +- .../style/values/specified/transform.rs | 21 +- servo/ports/geckolib/glue.rs | 6 +- 9 files changed, 457 insertions(+), 401 deletions(-) diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs index 022507fdce0d..6cd1b509b06f 100644 --- a/servo/components/style/properties/gecko.mako.rs +++ b/servo/components/style/properties/gecko.mako.rs @@ -979,6 +979,400 @@ def set_gecko_property(ffi_name, expr): } +<% +transform_functions = [ + ("Matrix3D", "matrix3d", ["number"] * 16), + ("PrefixedMatrix3D", "matrix3d", ["number"] * 12 + ["lopon"] * 2 + + ["lon"] + ["number"]), + ("Matrix", "matrix", ["number"] * 6), + ("PrefixedMatrix", "matrix", ["number"] * 4 + ["lopon"] * 2), + ("Translate", "translate", ["lop", "optional_lop"]), + ("Translate3D", "translate3d", ["lop", "lop", "length"]), + ("TranslateX", "translatex", ["lop"]), + ("TranslateY", "translatey", ["lop"]), + ("TranslateZ", "translatez", ["length"]), + ("Scale3D", "scale3d", ["number"] * 3), + ("Scale", "scale", ["number", "optional_number"]), + ("ScaleX", "scalex", ["number"]), + ("ScaleY", "scaley", ["number"]), + ("ScaleZ", "scalez", ["number"]), + ("Rotate", "rotate", ["angle"]), + ("Rotate3D", "rotate3d", ["number"] * 3 + ["angle"]), + ("RotateX", "rotatex", ["angle"]), + ("RotateY", "rotatey", ["angle"]), + ("RotateZ", "rotatez", ["angle"]), + ("Skew", "skew", ["angle", "optional_angle"]), + ("SkewX", "skewx", ["angle"]), + ("SkewY", "skewy", ["angle"]), + ("Perspective", "perspective", ["length"]), + ("InterpolateMatrix", "interpolatematrix", ["list"] * 2 + ["percentage"]), + ("AccumulateMatrix", "accumulatematrix", ["list"] * 2 + ["integer_to_percentage"]) +] +%> + +<%def name="transform_function_arm(name, keyword, items)"> + <% + has_optional = items[-1].startswith("optional_") + pattern = None + if keyword == "matrix3d": + # m11: number1, m12: number2, .. + single_patterns = ["m%s: %s" % (str(a / 4 + 1) + str(a % 4 + 1), b + str(a + 1)) for (a, b) + in enumerate(items)] + pattern = "(Matrix3D { %s })" % ", ".join(single_patterns) + elif keyword == "matrix": + # a: number1, b: number2, .. + single_patterns = ["%s: %s" % (chr(ord('a') + a), b + str(a + 1)) for (a, b) + in enumerate(items)] + pattern = "(Matrix { %s })" % ", ".join(single_patterns) + elif keyword == "interpolatematrix": + pattern = " { from_list: ref list1, to_list: ref list2, progress: percentage3 }" + elif keyword == "accumulatematrix": + pattern = " { from_list: ref list1, to_list: ref list2, count: integer_to_percentage3 }" + else: + # Generate contents of pattern from items + pattern = "(%s)" % ", ".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_SetPixelLength(%s, %s.px())", + "percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s.0)", + # Note: This is an integer type, but we use it as a percentage value in Gecko, so + # need to cast it to f32. + "integer_to_percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s as f32)", + "lop" : "%s.set_lop(%s)", + "lopon" : "set_lopon(%s, %s)", + "lon" : "set_lon(%s, %s)", + "angle" : "%s.set_angle(%s)", + "number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)", + # Note: We use nsCSSValueSharedList here, instead of nsCSSValueList_heap + # because this function is not called on the main thread and + # nsCSSValueList_heap is not thread safe. + "list" : "%s.set_shared_list(%s.0.iter().map(&convert_to_ns_css_value));", + } + %> + ::values::generics::transform::TransformOperation::${name}${pattern} => { + % if has_optional: + let optional_present = ${items[-1] + str(len(items))}.is_some(); + let len = if optional_present { + ${len(items) + 1} + } else { + ${len(items)} + }; + % else: + let len = ${len(items) + 1}; + % endif + bindings::Gecko_CSSValue_SetFunction(gecko_value, len); + bindings::Gecko_CSSValue_SetKeyword( + bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0), + structs::nsCSSKeyword::eCSSKeyword_${keyword} + ); + % for index, item in enumerate(items): + <% replaced_item = item.replace("optional_", "") %> + % if item.startswith("optional"): + if let Some(${replaced_item + str(index + 1)}) = ${item + str(index + 1)} { + % endif + % if item == "list": + debug_assert!(!${item}${index + 1}.0.is_empty()); + % endif + ${css_value_setters[replaced_item] % ( + "bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d)" % (index + 1), + replaced_item + str(index + 1) + )}; + % if item.startswith("optional"): + } + % endif + % endfor + } + + +<%def name="computed_operation_arm(name, keyword, items)"> + <% + # %s is substituted with the call to GetArrayItem. + css_value_getters = { + "length" : "Length::new(bindings::Gecko_CSSValue_GetNumber(%s))", + "lop" : "%s.get_lop()", + "lopon" : "Either::Second(%s.get_lop())", + "lon" : "Either::First(%s.get_length())", + "angle" : "%s.get_angle()", + "number" : "bindings::Gecko_CSSValue_GetNumber(%s)", + "percentage" : "Percentage(bindings::Gecko_CSSValue_GetPercentage(%s))", + "integer_to_percentage" : "bindings::Gecko_CSSValue_GetPercentage(%s) as i32", + "list" : "Transform(convert_shared_list_to_operations(%s))", + } + pre_symbols = "(" + post_symbols = ")" + if keyword == "interpolatematrix" or keyword == "accumulatematrix": + # We generate this like: "TransformOperation::InterpolateMatrix {", so the space is + # between "InterpolateMatrix"/"AccumulateMatrix" and '{' + pre_symbols = " {" + post_symbols = "}" + elif keyword == "matrix3d": + pre_symbols = "(Matrix3D {" + post_symbols = "})" + elif keyword == "matrix": + pre_symbols = "(Matrix {" + post_symbols = "})" + field_names = None + if keyword == "interpolatematrix": + field_names = ["from_list", "to_list", "progress"] + elif keyword == "accumulatematrix": + field_names = ["from_list", "to_list", "count"] + + %> + <% + + guard = "" + if name == "Matrix3D" or name == "Matrix": + guard = "if !needs_prefix " + elif name == "PrefixedMatrix3D" or name == "PrefixedMatrix": + guard = "if needs_prefix " + + %> + structs::nsCSSKeyword::eCSSKeyword_${keyword} ${guard}=> { + ::values::generics::transform::TransformOperation::${name}${pre_symbols} + % for index, item in enumerate(items): + % if keyword == "matrix3d": + m${index / 4 + 1}${index % 4 + 1}: + % elif keyword == "matrix": + ${chr(ord('a') + index)}: + % elif keyword == "interpolatematrix" or keyword == "accumulatematrix": + ${field_names[index]}: + % endif + <% + getter = css_value_getters[item.replace("optional_", "")] % ( + "bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d)" % (index + 1) + ) + %> + % if item.startswith("optional_"): + if (**gecko_value.mValue.mArray.as_ref()).mCount == ${index + 1} { + None + } else { + Some(${getter}) + } + % else: + ${getter} + % endif +, + % endfor + ${post_symbols} + }, + + +fn set_single_transform_function( + servo_value: &values::computed::TransformOperation, + gecko_value: &mut structs::nsCSSValue /* output */ +) { + use values::computed::{Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrNumber}; + use values::computed::TransformOperation; + use values::generics::transform::{Matrix, Matrix3D}; + + let convert_to_ns_css_value = |item: &TransformOperation| -> structs::nsCSSValue { + let mut value = structs::nsCSSValue::null(); + set_single_transform_function(item, &mut value); + value + }; + + unsafe fn set_lopon(css: &mut structs::nsCSSValue, lopon: LengthOrPercentageOrNumber) { + let lop = match lopon { + Either::First(number) => LengthOrPercentage::Length(Length::new(number)), + Either::Second(lop) => lop, + }; + css.set_lop(lop); + } + + unsafe fn set_lon(css: &mut structs::nsCSSValue, lopon: LengthOrNumber) { + let length = match lopon { + Either::Second(number) => Length::new(number), + Either::First(l) => l, + }; + bindings::Gecko_CSSValue_SetPixelLength(css, length.px()) + } + + unsafe { + match *servo_value { + % for servo, gecko, format in transform_functions: + ${transform_function_arm(servo, gecko, format)} + % endfor + } + } +} + +pub fn convert_transform( + input: &[values::computed::TransformOperation], + output: &mut structs::root::RefPtr +) { + use gecko_bindings::sugar::refptr::RefPtr; + + unsafe { output.clear() }; + + let list = unsafe { + RefPtr::from_addrefed(bindings::Gecko_NewCSSValueSharedList(input.len() as u32)) + }; + let value_list = unsafe { list.mHead.as_mut() }; + if let Some(value_list) = value_list { + for (gecko, servo) in value_list.into_iter().zip(input.into_iter()) { + set_single_transform_function(servo, gecko); + } + } + unsafe { output.set_move(list) }; +} + +fn clone_single_transform_function( + gecko_value: &structs::nsCSSValue +) -> values::computed::TransformOperation { + use values::computed::{Length, Percentage, TransformOperation}; + use values::generics::transform::{Matrix, Matrix3D}; + use values::generics::transform::Transform; + + let convert_shared_list_to_operations = |value: &structs::nsCSSValue| + -> Vec { + debug_assert!(value.mUnit == structs::nsCSSUnit::eCSSUnit_SharedList); + let value_list = unsafe { + value.mValue.mSharedList.as_ref() + .as_mut().expect("List pointer should be non-null").mHead.as_ref() + }; + debug_assert!(value_list.is_some(), "An empty shared list is not allowed"); + value_list.unwrap().into_iter() + .map(|item| clone_single_transform_function(item)) + .collect() + }; + + let transform_function = unsafe { + bindings::Gecko_CSSValue_GetKeyword(bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 0)) + }; + + let needs_prefix = if transform_function == structs::nsCSSKeyword::eCSSKeyword_matrix3d { + unsafe { + bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 13).mUnit + != structs::nsCSSUnit::eCSSUnit_Number || + bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 14).mUnit + != structs::nsCSSUnit::eCSSUnit_Number || + bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 15).mUnit + != structs::nsCSSUnit::eCSSUnit_Number + } + } else { + false + }; + + unsafe { + match transform_function { + % for servo, gecko, format in transform_functions: + ${computed_operation_arm(servo, gecko, format)} + % endfor + _ => panic!("{:?} is not an acceptable transform function", transform_function), + } + } +} + +pub fn clone_transform_from_list( + list: Option< &structs::root::nsCSSValueList> +) -> values::computed::Transform { + use values::generics::transform::Transform; + + let result = match list { + Some(list) => { + list.into_iter() + .filter_map(|value| { + // Handle none transform. + if value.is_none() { + None + } else { + Some(clone_single_transform_function(value)) + } + }) + .collect::>() + }, + _ => vec![], + }; + Transform(result) +} + +<%def name="impl_transform(ident, gecko_ffi_name)"> + #[allow(non_snake_case)] + pub fn set_${ident}(&mut self, other: values::computed::Transform) { + use gecko_properties::convert_transform; + if other.0.is_empty() { + unsafe { + self.gecko.${gecko_ffi_name}.clear(); + } + return; + }; + convert_transform(&other.0, &mut self.gecko.${gecko_ffi_name}); + } + + #[allow(non_snake_case)] + pub fn copy_${ident}_from(&mut self, other: &Self) { + unsafe { self.gecko.${gecko_ffi_name}.set(&other.gecko.${gecko_ffi_name}); } + } + + #[allow(non_snake_case)] + pub fn reset_${ident}(&mut self, other: &Self) { + self.copy_${ident}_from(other) + } + + #[allow(non_snake_case)] + pub fn clone_${ident}(&self) -> values::computed::Transform { + use gecko_properties::clone_transform_from_list; + use values::generics::transform::Transform; + + if self.gecko.${gecko_ffi_name}.mRawPtr.is_null() { + return Transform(vec!()); + } + let list = unsafe { (*self.gecko.${gecko_ffi_name}.to_safe().get()).mHead.as_ref() }; + clone_transform_from_list(list) + } + + +<%def name="impl_transform_origin(ident, gecko_ffi_name)"> + #[allow(non_snake_case)] + pub fn set_${ident}(&mut self, v: values::computed::TransformOrigin) { + self.gecko.${gecko_ffi_name}[0].set(v.horizontal); + self.gecko.${gecko_ffi_name}[1].set(v.vertical); + // transform-origin supports the third value for depth, while + // -moz-window-transform-origin doesn't. The following code is + // for handling this difference. Rust (incorrectly) generates + // an unsuppressible warning, but we know it's safe here. + // See rust-lang/rust#45850. Also if we can have more knowledge + // about the type here, we may want to check that the length is + // exactly either 2 or 3 in compile time. + if self.gecko.${gecko_ffi_name}.len() == 3 { + self.gecko.${gecko_ffi_name}[2].set(v.depth); + } + } + + #[allow(non_snake_case)] + pub fn copy_${ident}_from(&mut self, other: &Self) { + self.gecko.${gecko_ffi_name}[0].copy_from(&other.gecko.${gecko_ffi_name}[0]); + self.gecko.${gecko_ffi_name}[1].copy_from(&other.gecko.${gecko_ffi_name}[1]); + if self.gecko.${gecko_ffi_name}.len() == 3 { + self.gecko.${gecko_ffi_name}[2].copy_from(&other.gecko.${gecko_ffi_name}[2]); + } + } + + #[allow(non_snake_case)] + pub fn reset_${ident}(&mut self, other: &Self) { + self.copy_${ident}_from(other) + } + + #[allow(non_snake_case)] + pub fn clone_${ident}(&self) -> values::computed::TransformOrigin { + use values::computed::{Length, LengthOrPercentage, TransformOrigin}; + TransformOrigin { + horizontal: LengthOrPercentage::from_gecko_style_coord(&self.gecko.${gecko_ffi_name}[0]) + .expect("clone for LengthOrPercentage failed"), + vertical: LengthOrPercentage::from_gecko_style_coord(&self.gecko.${gecko_ffi_name}[1]) + .expect("clone for LengthOrPercentage failed"), + depth: if self.gecko.${gecko_ffi_name}.len() == 3 { + Length::from_gecko_style_coord(&self.gecko.${gecko_ffi_name}[2]) + .expect("clone for Length failed") + } else { + Length::new(0.) + }, + } + } + + <%def name="impl_logical(name, **kwargs)"> ${helpers.logical_setter(name)} @@ -1131,6 +1525,8 @@ impl Clone for ${style_struct.gecko_struct_name} { "SVGOpacity": impl_svg_opacity, "SVGPaint": impl_svg_paint, "SVGWidth": impl_svg_length, + "Transform": impl_transform, + "TransformOrigin": impl_transform_origin, "UrlOrNone": impl_css_url, } @@ -2699,9 +3095,9 @@ fn static_assert() { transition-duration transition-delay transition-timing-function transition-property page-break-before page-break-after - scroll-snap-points-x scroll-snap-points-y transform + scroll-snap-points-x scroll-snap-points-y scroll-snap-type-x scroll-snap-type-y scroll-snap-coordinate - perspective-origin transform-origin -moz-binding will-change + perspective-origin -moz-binding will-change shape-outside contain touch-action""" %> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}"> @@ -2894,331 +3290,6 @@ fn static_assert() { } ${impl_css_url('_moz_binding', 'mBinding.mPtr')} - <% - transform_functions = [ - ("Matrix3D", "matrix3d", ["number"] * 16), - ("PrefixedMatrix3D", "matrix3d", ["number"] * 12 + ["lopon"] * 2 - + ["lon"] + ["number"]), - ("Matrix", "matrix", ["number"] * 6), - ("PrefixedMatrix", "matrix", ["number"] * 4 + ["lopon"] * 2), - ("Translate", "translate", ["lop", "optional_lop"]), - ("Translate3D", "translate3d", ["lop", "lop", "length"]), - ("TranslateX", "translatex", ["lop"]), - ("TranslateY", "translatey", ["lop"]), - ("TranslateZ", "translatez", ["length"]), - ("Scale3D", "scale3d", ["number"] * 3), - ("Scale", "scale", ["number", "optional_number"]), - ("ScaleX", "scalex", ["number"]), - ("ScaleY", "scaley", ["number"]), - ("ScaleZ", "scalez", ["number"]), - ("Rotate", "rotate", ["angle"]), - ("Rotate3D", "rotate3d", ["number"] * 3 + ["angle"]), - ("RotateX", "rotatex", ["angle"]), - ("RotateY", "rotatey", ["angle"]), - ("RotateZ", "rotatez", ["angle"]), - ("Skew", "skew", ["angle", "optional_angle"]), - ("SkewX", "skewx", ["angle"]), - ("SkewY", "skewy", ["angle"]), - ("Perspective", "perspective", ["length"]), - ("InterpolateMatrix", "interpolatematrix", ["list"] * 2 + ["percentage"]), - ("AccumulateMatrix", "accumulatematrix", ["list"] * 2 + ["integer_to_percentage"]) - ] - %> - <%def name="transform_function_arm(name, keyword, items)"> - <% - has_optional = items[-1].startswith("optional_") - pattern = None - if keyword == "matrix3d": - # m11: number1, m12: number2, .. - single_patterns = ["m%s: %s" % (str(a / 4 + 1) + str(a % 4 + 1), b + str(a + 1)) for (a, b) - in enumerate(items)] - pattern = "(Matrix3D { %s })" % ", ".join(single_patterns) - elif keyword == "matrix": - # a: number1, b: number2, .. - single_patterns = ["%s: %s" % (chr(ord('a') + a), b + str(a + 1)) for (a, b) - in enumerate(items)] - pattern = "(Matrix { %s })" % ", ".join(single_patterns) - elif keyword == "interpolatematrix": - pattern = " { from_list: ref list1, to_list: ref list2, progress: percentage3 }" - elif keyword == "accumulatematrix": - pattern = " { from_list: ref list1, to_list: ref list2, count: integer_to_percentage3 }" - else: - # Generate contents of pattern from items - pattern = "(%s)" % ", ".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_SetPixelLength(%s, %s.px())", - "percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s.0)", - # Note: This is an integer type, but we use it as a percentage value in Gecko, so - # need to cast it to f32. - "integer_to_percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s as f32)", - "lop" : "%s.set_lop(%s)", - "lopon" : "set_lopon(%s, %s)", - "lon" : "set_lon(%s, %s)", - "angle" : "%s.set_angle(%s)", - "number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)", - # Note: We use nsCSSValueSharedList here, instead of nsCSSValueList_heap - # because this function is not called on the main thread and - # nsCSSValueList_heap is not thread safe. - "list" : "%s.set_shared_list(%s.0.iter().map(&convert_to_ns_css_value));", - } - %> - ::values::generics::transform::TransformOperation::${name}${pattern} => { - % if has_optional: - let optional_present = ${items[-1] + str(len(items))}.is_some(); - let len = if optional_present { - ${len(items) + 1} - } else { - ${len(items)} - }; - % else: - let len = ${len(items) + 1}; - % endif - bindings::Gecko_CSSValue_SetFunction(gecko_value, len); - bindings::Gecko_CSSValue_SetKeyword( - bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0), - structs::nsCSSKeyword::eCSSKeyword_${keyword} - ); - % for index, item in enumerate(items): - <% replaced_item = item.replace("optional_", "") %> - % if item.startswith("optional"): - if let Some(${replaced_item + str(index + 1)}) = ${item + str(index + 1)} { - % endif - % if item == "list": - debug_assert!(!${item}${index + 1}.0.is_empty()); - % endif - ${css_value_setters[replaced_item] % ( - "bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d)" % (index + 1), - replaced_item + str(index + 1) - )}; - % if item.startswith("optional"): - } - % endif - % endfor - } - - fn set_single_transform_function(servo_value: &longhands::transform::computed_value::ComputedOperation, - gecko_value: &mut structs::nsCSSValue /* output */) { - use properties::longhands::transform::computed_value::ComputedOperation; - use values::computed::{Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrNumber}; - use values::generics::transform::{Matrix, Matrix3D}; - - let convert_to_ns_css_value = |item: &ComputedOperation| -> structs::nsCSSValue { - let mut value = structs::nsCSSValue::null(); - Self::set_single_transform_function(item, &mut value); - value - }; - - unsafe fn set_lopon(css: &mut structs::nsCSSValue, lopon: LengthOrPercentageOrNumber) { - let lop = match lopon { - Either::First(number) => LengthOrPercentage::Length(Length::new(number)), - Either::Second(lop) => lop, - }; - css.set_lop(lop); - } - - unsafe fn set_lon(css: &mut structs::nsCSSValue, lopon: LengthOrNumber) { - let length = match lopon { - Either::Second(number) => Length::new(number), - Either::First(l) => l, - }; - bindings::Gecko_CSSValue_SetPixelLength(css, length.px()) - } - - unsafe { - match *servo_value { - % for servo, gecko, format in transform_functions: - ${transform_function_arm(servo, gecko, format)} - % endfor - } - } - } - pub fn convert_transform(input: &[longhands::transform::computed_value::ComputedOperation], - output: &mut structs::root::RefPtr) { - use gecko_bindings::sugar::refptr::RefPtr; - - unsafe { output.clear() }; - - let list = unsafe { - RefPtr::from_addrefed(bindings::Gecko_NewCSSValueSharedList(input.len() as u32)) - }; - let value_list = unsafe { list.mHead.as_mut() }; - if let Some(value_list) = value_list { - for (gecko, servo) in value_list.into_iter().zip(input.into_iter()) { - Self::set_single_transform_function(servo, gecko); - } - } - unsafe { output.set_move(list) }; - } - - pub fn set_transform(&mut self, other: longhands::transform::computed_value::T) { - if other.0.is_empty() { - unsafe { - self.gecko.mSpecifiedTransform.clear(); - } - return; - }; - Self::convert_transform(&other.0, &mut self.gecko.mSpecifiedTransform); - } - - pub fn copy_transform_from(&mut self, other: &Self) { - unsafe { self.gecko.mSpecifiedTransform.set(&other.gecko.mSpecifiedTransform); } - } - - pub fn reset_transform(&mut self, other: &Self) { - self.copy_transform_from(other) - } - - <%def name="computed_operation_arm(name, keyword, items)"> - <% - # %s is substituted with the call to GetArrayItem. - css_value_getters = { - "length" : "Length::new(bindings::Gecko_CSSValue_GetNumber(%s))", - "lop" : "%s.get_lop()", - "lopon" : "Either::Second(%s.get_lop())", - "lon" : "Either::First(%s.get_length())", - "angle" : "%s.get_angle()", - "number" : "bindings::Gecko_CSSValue_GetNumber(%s)", - "percentage" : "Percentage(bindings::Gecko_CSSValue_GetPercentage(%s))", - "integer_to_percentage" : "bindings::Gecko_CSSValue_GetPercentage(%s) as i32", - "list" : "Transform(convert_shared_list_to_operations(%s))", - } - pre_symbols = "(" - post_symbols = ")" - if keyword == "interpolatematrix" or keyword == "accumulatematrix": - # We generate this like: "ComputedOperation::InterpolateMatrix {", so the space is - # between "InterpolateMatrix"/"AccumulateMatrix" and '{' - pre_symbols = " {" - post_symbols = "}" - elif keyword == "matrix3d": - pre_symbols = "(Matrix3D {" - post_symbols = "})" - elif keyword == "matrix": - pre_symbols = "(Matrix {" - post_symbols = "})" - field_names = None - if keyword == "interpolatematrix": - field_names = ["from_list", "to_list", "progress"] - elif keyword == "accumulatematrix": - field_names = ["from_list", "to_list", "count"] - - %> - <% - - guard = "" - if name == "Matrix3D" or name == "Matrix": - guard = "if !needs_prefix " - elif name == "PrefixedMatrix3D" or name == "PrefixedMatrix": - guard = "if needs_prefix " - - %> - structs::nsCSSKeyword::eCSSKeyword_${keyword} ${guard}=> { - ::values::generics::transform::TransformOperation::${name}${pre_symbols} - % for index, item in enumerate(items): - % if keyword == "matrix3d": - m${index / 4 + 1}${index % 4 + 1}: - % elif keyword == "matrix": - ${chr(ord('a') + index)}: - % elif keyword == "interpolatematrix" or keyword == "accumulatematrix": - ${field_names[index]}: - % endif - <% - getter = css_value_getters[item.replace("optional_", "")] % ( - "bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d)" % (index + 1) - ) - %> - % if item.startswith("optional_"): - if (**gecko_value.mValue.mArray.as_ref()).mCount == ${index + 1} { - None - } else { - Some(${getter}) - } - % else: - ${getter} - % endif -, - % endfor - ${post_symbols} - }, - - fn clone_single_transform_function(gecko_value: &structs::nsCSSValue) - -> longhands::transform::computed_value::ComputedOperation { - use properties::longhands::transform::computed_value::ComputedOperation; - use values::computed::{Length, Percentage}; - use values::generics::transform::{Matrix, Matrix3D}; - use values::generics::transform::Transform; - - let convert_shared_list_to_operations = |value: &structs::nsCSSValue| - -> Vec { - debug_assert!(value.mUnit == structs::nsCSSUnit::eCSSUnit_SharedList); - let value_list = unsafe { - value.mValue.mSharedList.as_ref() - .as_mut().expect("List pointer should be non-null").mHead.as_ref() - }; - debug_assert!(value_list.is_some(), "An empty shared list is not allowed"); - value_list.unwrap().into_iter() - .map(|item| Self::clone_single_transform_function(item)) - .collect() - }; - - let transform_function = unsafe { - bindings::Gecko_CSSValue_GetKeyword(bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 0)) - }; - - let needs_prefix = if transform_function == structs::nsCSSKeyword::eCSSKeyword_matrix3d { - unsafe { - bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 13).mUnit - != structs::nsCSSUnit::eCSSUnit_Number || - bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 14).mUnit - != structs::nsCSSUnit::eCSSUnit_Number || - bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 15).mUnit - != structs::nsCSSUnit::eCSSUnit_Number - } - } else { - false - }; - - unsafe { - match transform_function { - % for servo, gecko, format in transform_functions: - ${computed_operation_arm(servo, gecko, format)} - % endfor - _ => panic!("{:?} is not an acceptable transform function", transform_function), - } - } - } - pub fn clone_transform(&self) -> longhands::transform::computed_value::T { - use values::generics::transform::Transform; - - if self.gecko.mSpecifiedTransform.mRawPtr.is_null() { - return Transform(vec!()); - } - let list = unsafe { (*self.gecko.mSpecifiedTransform.to_safe().get()).mHead.as_ref() }; - Self::clone_transform_from_list(list) - } - pub fn clone_transform_from_list(list: Option< &structs::root::nsCSSValueList>) - -> longhands::transform::computed_value::T { - use values::generics::transform::Transform; - - let result = match list { - Some(list) => { - list.into_iter() - .filter_map(|value| { - // Handle none transform. - if value.is_none() { - None - } else { - Some(Self::clone_single_transform_function(value)) - } - }) - .collect::>() - }, - _ => vec![], - }; - Transform(result) - } ${impl_transition_time_value('delay', 'Delay')} ${impl_transition_time_value('duration', 'Duration')} @@ -3439,35 +3510,6 @@ fn static_assert() { } } - pub fn set_transform_origin(&mut self, v: longhands::transform_origin::computed_value::T) { - self.gecko.mTransformOrigin[0].set(v.horizontal); - self.gecko.mTransformOrigin[1].set(v.vertical); - self.gecko.mTransformOrigin[2].set(v.depth); - } - - pub fn copy_transform_origin_from(&mut self, other: &Self) { - self.gecko.mTransformOrigin[0].copy_from(&other.gecko.mTransformOrigin[0]); - self.gecko.mTransformOrigin[1].copy_from(&other.gecko.mTransformOrigin[1]); - self.gecko.mTransformOrigin[2].copy_from(&other.gecko.mTransformOrigin[2]); - } - - pub fn reset_transform_origin(&mut self, other: &Self) { - self.copy_transform_origin_from(other) - } - - pub fn clone_transform_origin(&self) -> longhands::transform_origin::computed_value::T { - use properties::longhands::transform_origin::computed_value::T; - use values::computed::{Length, LengthOrPercentage}; - T { - horizontal: LengthOrPercentage::from_gecko_style_coord(&self.gecko.mTransformOrigin[0]) - .expect("clone for LengthOrPercentage failed"), - vertical: LengthOrPercentage::from_gecko_style_coord(&self.gecko.mTransformOrigin[1]) - .expect("clone for LengthOrPercentage failed"), - depth: Length::from_gecko_style_coord(&self.gecko.mTransformOrigin[2]) - .expect("clone for Length failed"), - } - } - pub fn set_will_change(&mut self, v: longhands::will_change::computed_value::T) { use gecko_bindings::bindings::{Gecko_AppendWillChange, Gecko_ClearWillChange}; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_OPACITY; diff --git a/servo/components/style/properties/longhand/box.mako.rs b/servo/components/style/properties/longhand/box.mako.rs index 4d13abedf060..f5f9d0753cdc 100644 --- a/servo/components/style/properties/longhand/box.mako.rs +++ b/servo/components/style/properties/longhand/box.mako.rs @@ -567,42 +567,13 @@ ${helpers.predefined_type( allow_empty="NotInitial" )} -<%helpers:longhand name="transform" extra_prefixes="webkit" - animation_value_type="ComputedValue" - flags="CREATES_STACKING_CONTEXT FIXPOS_CB" - spec="https://drafts.csswg.org/css-transforms/#propdef-transform"> - use values::generics::transform::Transform; - - - pub mod computed_value { - pub use values::computed::transform::Transform as T; - pub use values::computed::transform::TransformOperation as ComputedOperation; - } - - pub use values::specified::transform::Transform as SpecifiedValue; - pub use values::specified::transform::TransformOperation as SpecifiedOperation; - - #[inline] - pub fn get_initial_value() -> computed_value::T { - Transform(vec![]) - } - - - /// Parses `transform` property. - #[inline] - pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) - -> Result> { - SpecifiedValue::parse_internal(context, input, false) - } - - /// Parses `-moz-transform` property. This prefixed property also accepts LengthOrPercentage - /// in the nondiagonal homogeneous components of matrix and matrix3d. - #[inline] - pub fn parse_prefixed<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) - -> Result> { - SpecifiedValue::parse_internal(context, input, true) - } - +${helpers.predefined_type("transform", "Transform", + "generics::transform::Transform::none()", + extra_prefixes="webkit", + animation_value_type="ComputedValue", + gecko_ffi_name="mSpecifiedTransform", + flags="CREATES_STACKING_CONTEXT FIXPOS_CB", + spec="https://drafts.csswg.org/css-transforms/#propdef-transform")} // CSSOM View Module // https://www.w3.org/TR/cssom-view-1/ @@ -713,6 +684,7 @@ ${helpers.predefined_type("transform-origin", "computed::TransformOrigin::initial_value()", animation_value_type="ComputedValue", extra_prefixes="moz webkit", + gecko_ffi_name="mTransformOrigin", boxed=True, spec="https://drafts.csswg.org/css-transforms/#transform-origin-property")} diff --git a/servo/components/style/properties/longhand/ui.mako.rs b/servo/components/style/properties/longhand/ui.mako.rs index 6152411155a3..98093882e175 100644 --- a/servo/components/style/properties/longhand/ui.mako.rs +++ b/servo/components/style/properties/longhand/ui.mako.rs @@ -46,6 +46,23 @@ ${helpers.predefined_type("-moz-window-opacity", "Opacity", "1.0", products="gec internal=True, spec="None (Nonstandard internal property)")} +${helpers.predefined_type("-moz-window-transform", "Transform", + "generics::transform::Transform::none()", + products="gecko", gecko_ffi_name="mSpecifiedWindowTransform", + animation_value_type="ComputedValue", + internal=True, + spec="None (Nonstandard internal property)")} + +${helpers.predefined_type("-moz-window-transform-origin", + "TransformOrigin", + "computed::TransformOrigin::initial_value()", + animation_value_type="ComputedValue", + gecko_ffi_name="mWindowTransformOrigin", + products="gecko", + boxed=True, + internal=True, + spec="None (Nonstandard internal property)")} + <%helpers:longhand name="-moz-force-broken-image-icon" products="gecko" animation_value_type="discrete" diff --git a/servo/components/style/properties/shorthand/box.mako.rs b/servo/components/style/properties/shorthand/box.mako.rs index 94510e12c1f6..4a058b5b7abd 100644 --- a/servo/components/style/properties/shorthand/box.mako.rs +++ b/servo/components/style/properties/shorthand/box.mako.rs @@ -363,12 +363,11 @@ macro_rules! try_parse_one { flags="SHORTHAND_ALIAS_PROPERTY" derive_serialize="True" spec="Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/transform"> - use properties::longhands::transform; - pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { + use values::specified::transform::Transform; Ok(expanded! { - transform: transform::parse_prefixed(context, input)?, + transform: Transform::parse_prefixed(context, input)?, }) } diff --git a/servo/components/style/values/computed/mod.rs b/servo/components/style/values/computed/mod.rs index 7c6f54f58edb..bf3bbf712c5a 100644 --- a/servo/components/style/values/computed/mod.rs +++ b/servo/components/style/values/computed/mod.rs @@ -57,7 +57,7 @@ pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDash pub use self::table::XSpan; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextOverflow, WordSpacing}; pub use self::time::Time; -pub use self::transform::{TimingFunction, TransformOrigin}; +pub use self::transform::{TimingFunction, Transform, TransformOperation, TransformOrigin}; #[cfg(feature = "gecko")] pub mod align; diff --git a/servo/components/style/values/generics/transform.rs b/servo/components/style/values/generics/transform.rs index 45d19c8ab68d..c4c410700659 100644 --- a/servo/components/style/values/generics/transform.rs +++ b/servo/components/style/values/generics/transform.rs @@ -450,3 +450,10 @@ impl ToCss for Transform { Ok(()) } } + +impl Transform { + /// `none` + pub fn none() -> Self { + Transform(vec![]) + } +} diff --git a/servo/components/style/values/specified/mod.rs b/servo/components/style/values/specified/mod.rs index 9a33019d98a9..3827b638e4b2 100644 --- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -53,7 +53,7 @@ pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDash pub use self::table::XSpan; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextOverflow, WordSpacing}; pub use self::time::Time; -pub use self::transform::{TimingFunction, TransformOrigin}; +pub use self::transform::{TimingFunction, Transform, TransformOrigin}; pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent; #[cfg(feature = "gecko")] diff --git a/servo/components/style/values/specified/transform.rs b/servo/components/style/values/specified/transform.rs index e01a5d996c53..ca77bd7a723f 100644 --- a/servo/components/style/values/specified/transform.rs +++ b/servo/components/style/values/specified/transform.rs @@ -39,7 +39,7 @@ pub type TransformOrigin = GenericTransformOrigin, OriginComp impl Transform { /// Internal parse function for deciding if we wish to accept prefixed values or not // Allow unitless zero angle for rotate() and skew() to align with gecko - pub fn parse_internal<'i, 't>( + fn parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, prefixed: bool, @@ -252,6 +252,25 @@ impl Transform { }) })?)) } + + /// Parses `-moz-transform` property. This prefixed property also accepts LengthOrPercentage + /// in the nondiagonal homogeneous components of matrix and matrix3d. + #[inline] + pub fn parse_prefixed<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + Transform::parse_internal(context, input, true) + } +} + +impl Parse for Transform { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result> { + Transform::parse_internal(context, input, false) + } } /// The specified value of a component of a CSS ``. diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs index 07d053796281..04ff917fa1e6 100644 --- a/servo/ports/geckolib/glue.rs +++ b/servo/ports/geckolib/glue.rs @@ -111,7 +111,7 @@ use style::gecko_bindings::structs::nsresult; use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI}; use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong}; use style::gecko_bindings::sugar::refptr::RefPtr; -use style::gecko_properties::style_structs; +use style::gecko_properties; use style::invalidation::element::restyle_hints; use style::media_queries::{Device, MediaList, parse_media_query_list}; use style::parser::{Parse, ParserContext, self}; @@ -721,7 +721,7 @@ pub extern "C" fn Servo_AnimationValue_GetTransform( list.set_move(RefPtr::from_addrefed(Gecko_NewNoneTransform())); } } else { - style_structs::Box::convert_transform(&servo_list.0, list); + gecko_properties::convert_transform(&servo_list.0, list); } } else { panic!("The AnimationValue should be transform"); @@ -733,7 +733,7 @@ pub extern "C" fn Servo_AnimationValue_Transform( list: *const nsCSSValueSharedList ) -> RawServoAnimationValueStrong { let list = unsafe { (&*list).mHead.as_ref() }; - let transform = style_structs::Box::clone_transform_from_list(list); + let transform = gecko_properties::clone_transform_from_list(list); Arc::new(AnimationValue::Transform(transform)).into_strong() }