aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2017-11-12 13:04:22 -0600
committerGitHub <noreply@github.com>2017-11-12 13:04:22 -0600
commitbc58e18761ef687bd6c5efcda635cabdb742069a (patch)
treee3a592e2fcdbd5b42c3c9b61eb7c22e6afd76224
parentfad4d7ae49140b8919f9b823542b440aeb0ecc9e (diff)
parent79ea63945851531dbf457f234bb239cc731fbb6e (diff)
downloadservo-bc58e18761ef687bd6c5efcda635cabdb742069a.tar.gz
servo-bc58e18761ef687bd6c5efcda635cabdb742069a.zip
Auto merge of #19141 - 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. 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? <!-- 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/19141) <!-- Reviewable:end -->
-rw-r--r--components/style/properties/gecko.mako.rs754
-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.rs21
-rw-r--r--ports/geckolib/glue.rs6
9 files changed, 457 insertions, 401 deletions
diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs
index 022507fdce0..6cd1b509b06 100644
--- a/components/style/properties/gecko.mako.rs
+++ b/components/style/properties/gecko.mako.rs
@@ -979,6 +979,400 @@ 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)">
+ #[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>
+
<%def name="impl_logical(name, **kwargs)">
${helpers.logical_setter(name)}
</%def>
@@ -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
- }
- </%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')}
@@ -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/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 7c6f54f58ed..bf3bbf712c5 100644
--- a/components/style/values/computed/mod.rs
+++ b/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/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 9a33019d98a..3827b638e4b 100644
--- a/components/style/values/specified/mod.rs
+++ b/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/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs
index e01a5d996c5..ca77bd7a723 100644
--- a/components/style/values/specified/transform.rs
+++ b/components/style/values/specified/transform.rs
@@ -39,7 +39,7 @@ pub type TransformOrigin = GenericTransformOrigin<OriginComponent<X>, 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<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 07d05379628..04ff917fa1e 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()
}