aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2017-11-06 17:46:51 -0600
committerGitHub <noreply@github.com>2017-11-06 17:46:51 -0600
commit4decea188feb87b880fd2b0663b86d262a346a18 (patch)
tree8e55dce1d251513654adb1dc4458c164f6a17cdc
parent5227df260c601b86358e08ab88adcc9eca93be45 (diff)
parent7dfcbb582a22f33f3c7381b93a7c168bdeae6600 (diff)
downloadservo-4decea188feb87b880fd2b0663b86d262a346a18.tar.gz
servo-4decea188feb87b880fd2b0663b86d262a346a18.zip
Auto merge of #19106 - upsuper:window-transform, r=emilio
Implement -moz-window-transform{,-origin} internal properties 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. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/19106) <!-- Reviewable:end -->
-rw-r--r--components/style/properties/gecko.mako.rs749
-rw-r--r--components/style/properties/longhand/box.mako.rs44
-rw-r--r--components/style/properties/longhand/ui.mako.rs17
-rw-r--r--components/style/properties/shorthand/box.mako.rs5
-rw-r--r--components/style/values/computed/mod.rs2
-rw-r--r--components/style/values/generics/transform.rs7
-rw-r--r--components/style/values/specified/mod.rs2
-rw-r--r--components/style/values/specified/transform.rs23
-rw-r--r--ports/geckolib/glue.rs6
9 files changed, 453 insertions, 402 deletions
diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs
index b79fd2aebb7..e362f324da2 100644
--- a/components/style/properties/gecko.mako.rs
+++ b/components/style/properties/gecko.mako.rs
@@ -978,6 +978,395 @@ def set_gecko_property(ffi_name, expr):
}
</%def>
+<%
+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>
+
+<%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}
+ },
+</%def>
+
+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<structs::root::nsCSSValueSharedList>
+) {
+ 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<TransformOperation> {
+ 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<_>>()
+ },
+ _ => 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>
+
+<%def name="impl_transform_origin(ident, gecko_ffi_name)">
+ <% has_depth = ident == "transform" %>
+
+ #[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);
+ % if has_depth:
+ self.gecko.${gecko_ffi_name}[2].set(v.depth);
+ % endif
+ }
+
+ #[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 has_depth:
+ self.gecko.${gecko_ffi_name}[2].copy_from(&other.gecko.${gecko_ffi_name}[2]);
+ % endif
+ }
+
+ #[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"),
+ % if has_depth:
+ depth: Length::from_gecko_style_coord(&self.gecko.${gecko_ffi_name}[2])
+ .expect("clone for Length failed"),
+ % else:
+ depth: Length::new(0.)
+ % endif
+ }
+ }
+</%def>
+
<%def name="impl_logical(name, **kwargs)">
${helpers.logical_setter(name)}
</%def>
@@ -1130,6 +1519,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,
}
@@ -2698,9 +3089,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}">
@@ -2893,331 +3284,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
- }
- </%def>
- 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<structs::root::nsCSSValueSharedList>) {
- 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}
- },
- </%def>
- 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<ComputedOperation> {
- 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<_>>()
- },
- _ => vec![],
- };
- Transform(result)
- }
${impl_transition_time_value('delay', 'Delay')}
${impl_transition_time_value('duration', 'Duration')}
@@ -3438,35 +3504,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/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs
index 4d13abedf06..f5f9d0753cd 100644
--- a/components/style/properties/longhand/box.mako.rs
+++ b/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,ParseError<'i>> {
- 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,ParseError<'i>> {
- SpecifiedValue::parse_internal(context, input, true)
- }
-</%helpers:longhand>
+${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/components/style/properties/longhand/ui.mako.rs b/components/style/properties/longhand/ui.mako.rs
index 6152411155a..98093882e17 100644
--- a/components/style/properties/longhand/ui.mako.rs
+++ b/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/components/style/properties/shorthand/box.mako.rs b/components/style/properties/shorthand/box.mako.rs
index 94510e12c1f..4a058b5b7ab 100644
--- a/components/style/properties/shorthand/box.mako.rs
+++ b/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<Longhands, ParseError<'i>> {
+ use values::specified::transform::Transform;
Ok(expanded! {
- transform: transform::parse_prefixed(context, input)?,
+ transform: Transform::parse_prefixed(context, input)?,
})
}
</%helpers:shorthand>
diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs
index c6b33d58748..0c9213f071b 100644
--- a/components/style/values/computed/mod.rs
+++ b/components/style/values/computed/mod.rs
@@ -56,7 +56,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/components/style/values/generics/transform.rs b/components/style/values/generics/transform.rs
index 45d19c8ab68..c4c41070065 100644
--- a/components/style/values/generics/transform.rs
+++ b/components/style/values/generics/transform.rs
@@ -450,3 +450,10 @@ impl<T: ToCss> ToCss for Transform<T> {
Ok(())
}
}
+
+impl<T> Transform<T> {
+ /// `none`
+ pub fn none() -> Self {
+ Transform(vec![])
+ }
+}
diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs
index ffe4d7ba59b..28190a5c656 100644
--- a/components/style/values/specified/mod.rs
+++ b/components/style/values/specified/mod.rs
@@ -52,7 +52,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/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs
index e01a5d996c5..0bb22848573 100644
--- a/components/style/values/specified/transform.rs
+++ b/components/style/values/specified/transform.rs
@@ -37,9 +37,9 @@ pub type TransformOrigin = GenericTransformOrigin<OriginComponent<X>, OriginComp
impl Transform {
- /// Internal parse function for deciding if we wish to accept prefixed values or not
+ // 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<Self, ParseError<'i>> {
+ Transform::parse_internal(context, input, true)
+ }
+}
+
+impl Parse for Transform {
+ fn parse<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>
+ ) -> Result<Self, ParseError<'i>> {
+ Transform::parse_internal(context, input, false)
+ }
}
/// The specified value of a component of a CSS `<transform-origin>`.
diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs
index bb443057b6d..1b355dea018 100644
--- a/ports/geckolib/glue.rs
+++ b/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()
}