Bug 1332657 - Part 2: Implement clone_transform. r?heycam,manishearth draft
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Fri, 03 Feb 2017 13:38:13 +0900
changeset 470040 7ea52c2caccbc9b8e56f4f71b3841eb3323e47df
parent 469446 661b3cee2f4d796e4d9b8a88bf058fbca8e7ba9e
child 470041 02c86d86b36ac7e5573f61cbccb43ef4aea44d00
push id43917
push userhikezoe@mozilla.com
push dateFri, 03 Feb 2017 04:44:01 +0000
reviewersheycam, manishearth
bugs1332657
milestone54.0a1
Bug 1332657 - Part 2: Implement clone_transform. r?heycam,manishearth The implementation of clone_transform is an adaptation of set_transform. MozReview-Commit-ID: ESE1ha0x666
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/nsCSSValue.cpp
layout/style/nsCSSValue.h
servo/components/style/gecko_bindings/sugar/ns_css_value.rs
servo/components/style/properties/gecko.mako.rs
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -1065,59 +1065,111 @@ Gecko_NewCSSValueSharedList(uint32_t aLe
 }
 
 void
 Gecko_CSSValue_SetAbsoluteLength(nsCSSValueBorrowedMut aCSSValue, nscoord aLen)
 {
   aCSSValue->SetIntegerCoordValue(aLen);
 }
 
+nscoord
+Gecko_CSSValue_GetAbsoluteLength(nsCSSValueBorrowed aCSSValue)
+{
+  // SetIntegerCoordValue() which is used in Gecko_CSSValue_SetAbsoluteLength()
+  // converts values by nsPresContext::AppUnitsToFloatCSSPixels() and stores
+  // values in eCSSUnit_Pixel unit. We need to convert the values back to app
+  // units by GetPixelLength().
+  MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Pixel,
+             "The unit should be eCSSUnit_Pixel");
+  return aCSSValue->GetPixelLength();
+}
+
 void
 Gecko_CSSValue_SetNumber(nsCSSValueBorrowedMut aCSSValue, float aNumber)
 {
   aCSSValue->SetFloatValue(aNumber, eCSSUnit_Number);
 }
 
+float
+Gecko_CSSValue_GetNumber(nsCSSValueBorrowed aCSSValue)
+{
+  return aCSSValue->GetFloatValue();
+}
+
 void
 Gecko_CSSValue_SetKeyword(nsCSSValueBorrowedMut aCSSValue, nsCSSKeyword aKeyword)
 {
   aCSSValue->SetEnumValue(aKeyword);
 }
 
+nsCSSKeyword
+Gecko_CSSValue_GetKeyword(nsCSSValueBorrowed aCSSValue)
+{
+  return aCSSValue->GetKeywordValue();
+}
+
 void
 Gecko_CSSValue_SetPercentage(nsCSSValueBorrowedMut aCSSValue, float aPercent)
 {
   aCSSValue->SetPercentValue(aPercent);
 }
 
+float
+Gecko_CSSValue_GetPercentage(nsCSSValueBorrowed aCSSValue)
+{
+  return aCSSValue->GetPercentValue();
+}
+
 void
 Gecko_CSSValue_SetAngle(nsCSSValueBorrowedMut aCSSValue, float aRadians)
 {
   aCSSValue->SetFloatValue(aRadians, eCSSUnit_Radian);
 }
 
+float
+Gecko_CSSValue_GetAngle(nsCSSValueBorrowed aCSSValue)
+{
+  // Unfortunately nsCSSValue.GetAngleValueInRadians() returns double,
+  // so we use GetAngleValue() instead.
+  MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Radian,
+             "The unit should be eCSSUnit_Radian");
+  return aCSSValue->GetAngleValue();
+}
+
 void
 Gecko_CSSValue_SetCalc(nsCSSValueBorrowedMut aCSSValue, nsStyleCoord::CalcValue aCalc)
 {
   aCSSValue->SetCalcValue(&aCalc);
 }
 
+nsStyleCoord::CalcValue
+Gecko_CSSValue_GetCalc(nsCSSValueBorrowed aCSSValue)
+{
+  return aCSSValue->GetCalcValue();
+}
+
 void
 Gecko_CSSValue_SetFunction(nsCSSValueBorrowedMut aCSSValue, int32_t aLen)
 {
   nsCSSValue::Array* arr = nsCSSValue::Array::Create(aLen);
   aCSSValue->SetArrayValue(arr, eCSSUnit_Function);
 }
 
 nsCSSValueBorrowedMut
 Gecko_CSSValue_GetArrayItem(nsCSSValueBorrowedMut aCSSValue, int32_t aIndex)
 {
   return &aCSSValue->GetArrayValue()->Item(aIndex);
 }
 
+nsCSSValueBorrowed
+Gecko_CSSValue_GetArrayItemConst(nsCSSValueBorrowed aCSSValue, int32_t aIndex)
+{
+  return &aCSSValue->GetArrayValue()->Item(aIndex);
+}
+
 
 bool
 Gecko_PropertyId_IsPrefEnabled(nsCSSPropertyID id)
 {
   return nsCSSProps::IsEnabled(id);
 }
 
 void
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -293,24 +293,35 @@ NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsSty
 
 nsCSSShadowArray* Gecko_NewCSSShadowArray(uint32_t len);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsCSSShadowArray, CSSShadowArray);
 
 nsStyleQuoteValues* Gecko_NewStyleQuoteValues(uint32_t len);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsStyleQuoteValues, QuoteValues);
 
 nsCSSValueSharedList* Gecko_NewCSSValueSharedList(uint32_t len);
+
+// Getter for nsCSSValue
+nsCSSValueBorrowedMut Gecko_CSSValue_GetArrayItem(nsCSSValueBorrowedMut css_value, int32_t index);
+// const version of the above function.
+nsCSSValueBorrowed Gecko_CSSValue_GetArrayItemConst(nsCSSValueBorrowed css_value, int32_t index);
+nscoord Gecko_CSSValue_GetAbsoluteLength(nsCSSValueBorrowed css_value);
+float Gecko_CSSValue_GetAngle(nsCSSValueBorrowed css_value);
+nsCSSKeyword Gecko_CSSValue_GetKeyword(nsCSSValueBorrowed aCSSValue);
+float Gecko_CSSValue_GetNumber(nsCSSValueBorrowed css_value);
+float Gecko_CSSValue_GetPercentage(nsCSSValueBorrowed css_value);
+nsStyleCoord::CalcValue Gecko_CSSValue_GetCalc(nsCSSValueBorrowed aCSSValue);
+
 void Gecko_CSSValue_SetAbsoluteLength(nsCSSValueBorrowedMut css_value, nscoord len);
 void Gecko_CSSValue_SetNumber(nsCSSValueBorrowedMut css_value, float number);
 void Gecko_CSSValue_SetKeyword(nsCSSValueBorrowedMut css_value, nsCSSKeyword keyword);
 void Gecko_CSSValue_SetPercentage(nsCSSValueBorrowedMut css_value, float percent);
 void Gecko_CSSValue_SetAngle(nsCSSValueBorrowedMut css_value, float radians);
 void Gecko_CSSValue_SetCalc(nsCSSValueBorrowedMut css_value, nsStyleCoord::CalcValue calc);
 void Gecko_CSSValue_SetFunction(nsCSSValueBorrowedMut css_value, int32_t len);
-nsCSSValueBorrowedMut Gecko_CSSValue_GetArrayItem(nsCSSValueBorrowedMut css_value, int32_t index);
 void Gecko_CSSValue_Drop(nsCSSValueBorrowedMut css_value);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsCSSValueSharedList, CSSValueSharedList);
 bool Gecko_PropertyId_IsPrefEnabled(nsCSSPropertyID id);
 
 const nsMediaFeature* Gecko_GetMediaFeatures();
 
 // Style-struct management.
 #define STYLE_STRUCT(name, checkdata_cb)                                       \
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -828,16 +828,56 @@ void nsCSSValue::SetCalcValue(const nsSt
     arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
     arr2->Item(0).SetIntegerCoordValue(aCalc->mLength);
     arr2->Item(1).SetPercentValue(aCalc->mPercent);
   }
 
   SetArrayValue(arr, eCSSUnit_Calc);
 }
 
+nsStyleCoord::CalcValue
+nsCSSValue::GetCalcValue() const
+{
+  MOZ_ASSERT(mUnit == eCSSUnit_Calc,
+             "The unit should be eCSSUnit_Calc");
+
+  const nsCSSValue::Array* array = GetArrayValue();
+  MOZ_ASSERT(array->Count() == 1,
+             "There should be a 1-length array");
+
+  const nsCSSValue& rootValue = array->Item(0);
+
+  nsStyleCoord::CalcValue result;
+
+  if (rootValue.GetUnit() == eCSSUnit_Pixel) {
+    result.mLength = rootValue.GetFloatValue();
+    result.mPercent = 0.0f;
+    result.mHasPercent = false;
+  } else {
+    MOZ_ASSERT(rootValue.GetUnit() == eCSSUnit_Calc_Plus,
+               "Calc unit should be eCSSUnit_Calc_Plus");
+
+    const nsCSSValue::Array *calcPlusArray = rootValue.GetArrayValue();
+    MOZ_ASSERT(array->Count() == 2,
+               "eCSSUnit_Calc_Plus should have a 2-length array");
+
+    const nsCSSValue& length = calcPlusArray->Item(0);
+    const nsCSSValue& percent = calcPlusArray->Item(1);
+    MOZ_ASSERT(length.GetUnit() == eCSSUnit_Pixel,
+               "The first value should be eCSSUnit_Pixel");
+    MOZ_ASSERT(percent.GetUnit() == eCSSUnit_Percent,
+               "The first value should be eCSSUnit_Percent");
+    result.mLength = length.GetFloatValue();
+    result.mPercent = percent.GetPercentValue();
+    result.mHasPercent = true;
+  }
+
+  return result;
+}
+
 void nsCSSValue::StartImageLoad(nsIDocument* aDocument) const
 {
   MOZ_ASSERT(eCSSUnit_URL == mUnit, "Not a URL value!");
   mozilla::css::ImageValue* image =
     new mozilla::css::ImageValue(mValue.mURL->GetURI(),
                                  mValue.mURL->mString,
                                  mValue.mURL->mBaseURI,
                                  mValue.mURL->mReferrer,
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -933,16 +933,18 @@ public:
   void SetNormalValue();
   void SetSystemFontValue();
   void SetDummyValue();
   void SetDummyInheritValue();
 
   // Converts an nsStyleCoord::CalcValue back into a CSSValue
   void SetCalcValue(const nsStyleCoord::CalcValue* aCalc);
 
+  nsStyleCoord::CalcValue GetCalcValue() const;
+
   // These are a little different - they allocate storage for you and
   // return a handle.
   nsCSSRect& SetRectValue();
   nsCSSValueList* SetListValue();
   nsCSSValuePairList* SetPairListValue();
 
   // These take ownership of the passed-in resource.
   void AdoptListValue(mozilla::UniquePtr<nsCSSValueList> aValue);
--- a/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
@@ -1,19 +1,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Little helpers for `nsCSSValue`.
 
+use app_units::Au;
 use gecko_bindings::bindings::Gecko_CSSValue_Drop;
+use gecko_bindings::bindings::Gecko_CSSValue_GetAbsoluteLength;
+use gecko_bindings::bindings::Gecko_CSSValue_GetCalc;
+use gecko_bindings::bindings::Gecko_CSSValue_GetPercentage;
+use gecko_bindings::bindings::Gecko_CSSValue_SetAbsoluteLength;
+use gecko_bindings::bindings::Gecko_CSSValue_SetCalc;
+use gecko_bindings::bindings::Gecko_CSSValue_SetPercentage;
 use gecko_bindings::structs::{nsCSSValue, nsCSSUnit, nsCSSValue_Array};
 use std::mem;
 use std::ops::Index;
 use std::slice;
+use values::computed::LengthOrPercentage;
 
 impl nsCSSValue {
     /// Create a CSSValue with null unit, useful to be used as a return value.
     #[inline]
     pub fn null() -> Self {
         unsafe { mem::zeroed() }
     }
 
@@ -37,16 +45,47 @@ impl nsCSSValue {
     /// builds.
     pub unsafe fn array_unchecked(&self) -> &nsCSSValue_Array {
         debug_assert!(nsCSSUnit::eCSSUnit_Array as u32 <= self.mUnit as u32 &&
                       self.mUnit as u32 <= nsCSSUnit::eCSSUnit_Calc_Divided as u32);
         let array = *self.mValue.mArray.as_ref();
         debug_assert!(!array.is_null());
         &*array
     }
+
+    /// Sets LengthOrPercentage value to this nsCSSValue.
+    pub unsafe fn set_lop(&mut self, lop: LengthOrPercentage) {
+        match lop {
+            LengthOrPercentage::Length(au) => {
+                Gecko_CSSValue_SetAbsoluteLength(self, au.0)
+            }
+            LengthOrPercentage::Percentage(pc) => {
+                Gecko_CSSValue_SetPercentage(self, pc)
+            }
+            LengthOrPercentage::Calc(calc) => {
+                Gecko_CSSValue_SetCalc(self, calc.into())
+            }
+        }
+    }
+
+    /// Returns LengthOrPercentage value.
+    pub unsafe fn get_lop(&self) -> LengthOrPercentage {
+        match self.mUnit {
+            nsCSSUnit::eCSSUnit_Pixel => {
+                LengthOrPercentage::Length(Au(Gecko_CSSValue_GetAbsoluteLength(self)))
+            },
+            nsCSSUnit::eCSSUnit_Percent => {
+                LengthOrPercentage::Percentage(Gecko_CSSValue_GetPercentage(self))
+            },
+            nsCSSUnit::eCSSUnit_Calc => {
+                LengthOrPercentage::Calc(Gecko_CSSValue_GetCalc(self).into())
+            },
+            x => panic!("The unit should not be {:?}", x),
+        }
+    }
 }
 
 impl Drop for nsCSSValue {
     fn drop(&mut self) {
         unsafe { Gecko_CSSValue_Drop(self) };
     }
 }
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1312,17 +1312,17 @@ fn static_assert() {
                 # Generate contents of pattern from items
                 pattern = ", ".join([b + str(a+1) for (a,b) in enumerate(items)])
 
             # First %s substituted with the call to GetArrayItem, the second
             # %s substituted with the corresponding variable
             css_value_setters = {
                 "length" : "bindings::Gecko_CSSValue_SetAbsoluteLength(%s, %s.0)",
                 "percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s)",
-                "lop" : "set_lop(%s, %s)",
+                "lop" : "%s.set_lop(%s)",
                 "angle" : "bindings::Gecko_CSSValue_SetAngle(%s, %s.0)",
                 "number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)",
             }
         %>
         ComputedOperation::${name.title()}(${pattern}) => {
             bindings::Gecko_CSSValue_SetFunction(gecko_value, ${len(items) + 1});
             bindings::Gecko_CSSValue_SetKeyword(
                 bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0),
@@ -1336,31 +1336,16 @@ fn static_assert() {
             % endfor
         }
     </%def>
     pub fn set_transform(&mut self, other: longhands::transform::computed_value::T) {
         use gecko_bindings::structs::nsCSSKeyword::*;
         use gecko_bindings::sugar::refptr::RefPtr;
         use properties::longhands::transform::computed_value::ComputedMatrix;
         use properties::longhands::transform::computed_value::ComputedOperation;
-        use values::computed::LengthOrPercentage;
-
-        unsafe fn set_lop(value: &mut structs::nsCSSValue, lop: LengthOrPercentage) {
-            match lop {
-                LengthOrPercentage::Length(au) => {
-                    bindings::Gecko_CSSValue_SetAbsoluteLength(value, au.0)
-                }
-                LengthOrPercentage::Percentage(pc) => {
-                    bindings::Gecko_CSSValue_SetPercentage(value, pc)
-                }
-                LengthOrPercentage::Calc(calc) => {
-                    bindings::Gecko_CSSValue_SetCalc(value, calc.into())
-                }
-            }
-        }
 
         let vec = if let Some(v) = other.0 {
             v
         } else {
             unsafe {
                 self.gecko.mSpecifiedTransform.clear();
             }
             return;
@@ -1391,16 +1376,81 @@ fn static_assert() {
         debug_assert!(iter.next().is_none());
         unsafe { self.gecko.mSpecifiedTransform.set_move(list) };
     }
 
     pub fn copy_transform_from(&mut self, other: &Self) {
         unsafe { self.gecko.mSpecifiedTransform.set(&other.gecko.mSpecifiedTransform); }
     }
 
+    <%def name="computed_operation_arm(name, keyword, items)">
+        <%
+            # %s is substituted with the call to GetArrayItem.
+            css_value_getters = {
+                "length" : "Au(bindings::Gecko_CSSValue_GetAbsoluteLength(%s))",
+                "lop" : "%s.get_lop()",
+                "angle" : "Angle(bindings::Gecko_CSSValue_GetAngle(%s))",
+                "number" : "bindings::Gecko_CSSValue_GetNumber(%s)",
+            }
+        %>
+        eCSSKeyword_${keyword} => {
+            ComputedOperation::${name.title()}(
+            % if name == "matrix":
+                ComputedMatrix {
+            % endif
+            % for index, item in enumerate(items):
+                % if name == "matrix":
+                    m${index / 4 + 1}${index % 4 + 1}:
+                % endif
+                ${css_value_getters[item] % (
+                    "bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d)" % (index + 1)
+                )},
+            % endfor
+            % if name == "matrix":
+                }
+            % endif
+            )
+        },
+    </%def>
+    pub fn clone_transform(&self) -> longhands::transform::computed_value::T {
+        use app_units::Au;
+        use gecko_bindings::structs::nsCSSKeyword::*;
+        use properties::longhands::transform::computed_value;
+        use properties::longhands::transform::computed_value::ComputedMatrix;
+        use properties::longhands::transform::computed_value::ComputedOperation;
+        use values::computed::Angle;
+
+        if self.gecko.mSpecifiedTransform.mRawPtr.is_null() {
+            return computed_value::T(None);
+        }
+
+        let mut result = vec![];
+        let mut cur = unsafe { (*self.gecko.mSpecifiedTransform.to_safe().get()).mHead };
+        while !cur.is_null() {
+            let gecko_value = unsafe { &(*cur).mValue };
+            let transform_function = unsafe {
+                bindings::Gecko_CSSValue_GetKeyword(bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 0))
+            };
+            let servo = unsafe {
+                match transform_function {
+                    ${computed_operation_arm("matrix", "matrix3d", ["number"] * 16)}
+                    ${computed_operation_arm("skew", "skew", ["angle"] * 2)}
+                    ${computed_operation_arm("translate", "translate3d", ["lop", "lop", "length"])}
+                    ${computed_operation_arm("scale", "scale3d", ["number"] * 3)}
+                    ${computed_operation_arm("rotate", "rotate3d", ["number"] * 3 + ["angle"])}
+                    ${computed_operation_arm("perspective", "perspective", ["length"])}
+                    _ => panic!("We shouldn't set any other transform function types"),
+                }
+            };
+            result.push(servo);
+            unsafe { cur = (&*cur).mNext };
+        }
+        computed_value::T(Some(result))
+    }
+
     pub fn set_animation_name(&mut self, v: longhands::animation_name::computed_value::T) {
         use nsstring::nsCString;
         unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) };
 
         if v.0.len() > 0 {
             self.gecko.mAnimationNameCount = v.0.len() as u32;
             for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) {
                 gecko.mName.assign_utf8(&nsCString::from(servo.0.to_string()));