Bug 1335998 - Part 3: Use IntoIterator for nsCSSValueList. draft
authorBoris Chiou <boris.chiou@gmail.com>
Thu, 01 Jun 2017 14:47:58 +0800
changeset 589578 f6613b687230a8fc9551209ce88bff8a4c5dd321
parent 589577 a975479666b4e9d07f04b8045d358d071b312012
child 589579 a8059bf7998db01b386a69a6518a654933bf86e7
push id62434
push userbmo:boris.chiou@gmail.com
push dateTue, 06 Jun 2017 12:26:50 +0000
bugs1335998
milestone55.0a1
Bug 1335998 - Part 3: Use IntoIterator for nsCSSValueList. Use rust iterator to iterate nsCSSValueList. MozReview-Commit-ID: A1W0QKin6qt
servo/components/style/gecko_bindings/sugar/ns_css_value.rs
servo/components/style/properties/gecko.mako.rs
--- a/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
@@ -3,18 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Little helpers for `nsCSSValue`.
 
 use app_units::Au;
 use gecko_bindings::bindings;
 use gecko_bindings::structs;
 use gecko_bindings::structs::{nsCSSValue, nsCSSUnit};
-use gecko_bindings::structs::{nsCSSValue_Array, nscolor};
+use gecko_bindings::structs::{nsCSSValue_Array, nsCSSValueList, nscolor};
 use gecko_string_cache::Atom;
+use std::marker::PhantomData;
 use std::mem;
 use std::ops::{Index, IndexMut};
 use std::slice;
 use values::computed::{Angle, LengthOrPercentage};
 use values::specified::url::SpecifiedUrl;
 
 impl nsCSSValue {
     /// Create a CSSValue with null unit, useful to be used as a return value.
@@ -220,29 +221,27 @@ impl nsCSSValue {
     /// This is only supported on the main thread.
     pub fn set_pair(&mut self, x: &nsCSSValue, y: &nsCSSValue) {
         unsafe { bindings::Gecko_CSSValue_SetPair(self, x, y) }
     }
 
     /// Set to a list value
     ///
     /// This is only supported on the main thread.
-    pub fn set_list<I>(&mut self, mut values: I) where I: ExactSizeIterator<Item=nsCSSValue> {
+    pub fn set_list<I>(&mut self, values: I) where I: ExactSizeIterator<Item=nsCSSValue> {
         debug_assert!(values.len() > 0, "Empty list is not supported");
         unsafe { bindings::Gecko_CSSValue_SetList(self, values.len() as u32); }
         debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_List);
-        let mut item_ptr = &mut unsafe {
+        let list: &mut structs::nsCSSValueList = &mut unsafe {
             self.mValue.mList.as_ref() // &*nsCSSValueList_heap
                 .as_mut().expect("List pointer should be non-null")
-        }._base as *mut structs::nsCSSValueList;
-        while let Some(item) = unsafe { item_ptr.as_mut() } {
-            item.mValue = values.next().expect("Values shouldn't have been exhausted");
-            item_ptr = item.mNext;
+        }._base;
+        for (item, new_value) in list.into_iter().zip(values) {
+            *item = new_value;
         }
-        debug_assert!(values.next().is_none(), "Values should have been exhausted");
     }
 
     /// Set to a pair list value
     ///
     /// This is only supported on the main thread.
     pub fn set_pair_list<I>(&mut self, mut values: I)
     where I: ExactSizeIterator<Item=(nsCSSValue, nsCSSValue)> {
         debug_assert!(values.len() > 0, "Empty list is not supported");
@@ -263,16 +262,74 @@ impl nsCSSValue {
 }
 
 impl Drop for nsCSSValue {
     fn drop(&mut self) {
         unsafe { bindings::Gecko_CSSValue_Drop(self) };
     }
 }
 
+/// Iterator of nsCSSValueList.
+#[allow(non_camel_case_types)]
+pub struct nsCSSValueListIterator<'a> {
+    current: Option<&'a nsCSSValueList>,
+}
+
+impl<'a> Iterator for nsCSSValueListIterator<'a> {
+    type Item = &'a nsCSSValue;
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.current {
+            Some(item) => {
+                self.current = unsafe { item.mNext.as_ref() };
+                Some(&item.mValue)
+            },
+            None => None
+        }
+    }
+}
+
+impl<'a> IntoIterator for &'a nsCSSValueList {
+    type Item = &'a nsCSSValue;
+    type IntoIter = nsCSSValueListIterator<'a>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        nsCSSValueListIterator { current: Some(self) }
+    }
+}
+
+/// Mutable Iterator of nsCSSValueList.
+#[allow(non_camel_case_types)]
+pub struct nsCSSValueListMutIterator<'a> {
+    current: *mut nsCSSValueList,
+    phantom: PhantomData<&'a mut nsCSSValue>,
+}
+
+impl<'a> Iterator for nsCSSValueListMutIterator<'a> {
+    type Item = &'a mut nsCSSValue;
+    fn next(&mut self) -> Option<Self::Item> {
+        match unsafe { self.current.as_mut() } {
+            Some(item) => {
+                self.current = item.mNext;
+                Some(&mut item.mValue)
+            },
+            None => None
+        }
+    }
+}
+
+impl<'a> IntoIterator for &'a mut nsCSSValueList {
+    type Item = &'a mut nsCSSValue;
+    type IntoIter = nsCSSValueListMutIterator<'a>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        nsCSSValueListMutIterator { current: self as *mut nsCSSValueList,
+                                    phantom: PhantomData }
+    }
+}
+
 impl nsCSSValue_Array {
     /// Return the length of this `nsCSSValue::Array`
     #[inline]
     pub fn len(&self) -> usize {
         self.mCount
     }
 
     #[inline]
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -2280,67 +2280,66 @@ fn static_assert() {
                 "angle" : "%s.set_angle(%s)",
                 "number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)",
             }
         %>
         longhands::transform::computed_value::ComputedOperation::${name}(${pattern}) => {
             bindings::Gecko_CSSValue_SetFunction(gecko_value, ${len(items) + 1});
             bindings::Gecko_CSSValue_SetKeyword(
                 bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0),
-                eCSSKeyword_${keyword}
+                structs::nsCSSKeyword::eCSSKeyword_${keyword}
             );
             % for index, item in enumerate(items):
                 ${css_value_setters[item] % (
                     "bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d)" % (index + 1),
                     item + str(index + 1)
                 )};
             % endfor
         }
     </%def>
+    fn set_single_transform_function(servo_value: &longhands::transform::computed_value::ComputedOperation,
+                                     gecko_value: &mut structs::nsCSSValue /* output */) {
+        use properties::longhands::transform::computed_value::ComputedMatrix;
+        use properties::longhands::transform::computed_value::ComputedMatrixWithPercents;
+
+        unsafe {
+            match *servo_value {
+                ${transform_function_arm("Matrix", "matrix3d", ["number"] * 16)}
+                ${transform_function_arm("MatrixWithPercents", "matrix3d", ["number"] * 12 + ["lop"] * 2
+                                         + ["length"] + ["number"])}
+                ${transform_function_arm("Skew", "skew", ["angle"] * 2)}
+                ${transform_function_arm("Translate", "translate3d", ["lop", "lop", "length"])}
+                ${transform_function_arm("Scale", "scale3d", ["number"] * 3)}
+                ${transform_function_arm("Rotate", "rotate3d", ["number"] * 3 + ["angle"])}
+                ${transform_function_arm("Perspective", "perspective", ["length"])}
+                _ => {
+                    // TODO: Convert ComputedOperation::InterpolateMatrix into
+                    //       eCSSKeyword_interpolatematrix, and convert
+                    //       ComputedOperation::AccumulateMatrix into
+                    //       eCSSKeyword_accumulatematrix in the patch series.
+                    gecko_value.mUnit = structs::nsCSSUnit::eCSSUnit_None;
+                }
+            }
+        }
+    }
     pub fn convert_transform(input: &[longhands::transform::computed_value::ComputedOperation],
                              output: &mut structs::root::RefPtr<structs::root::nsCSSValueSharedList>) {
-        use gecko_bindings::structs::nsCSSKeyword::*;
         use gecko_bindings::sugar::refptr::RefPtr;
-        use properties::longhands::transform::computed_value::ComputedMatrix;
-        use properties::longhands::transform::computed_value::ComputedMatrixWithPercents;
 
         unsafe { output.clear() };
 
         let list = unsafe {
             RefPtr::from_addrefed(bindings::Gecko_NewCSSValueSharedList(input.len() as u32))
         };
-
-        let mut cur = list.mHead;
-        let mut iter = input.into_iter();
-        while !cur.is_null() {
-            let gecko_value = unsafe { &mut (*cur).mValue };
-            let servo = iter.next().expect("Gecko_NewCSSValueSharedList should create a shared \
-                                            value list of the same length as the transform vector");
-            unsafe {
-                match *servo {
-                    ${transform_function_arm("Matrix", "matrix3d", ["number"] * 16)}
-                    ${transform_function_arm("MatrixWithPercents", "matrix3d", ["number"] * 12 + ["lop"] * 2
-                                             + ["length"] + ["number"])}
-                    ${transform_function_arm("Skew", "skew", ["angle"] * 2)}
-                    ${transform_function_arm("Translate", "translate3d", ["lop", "lop", "length"])}
-                    ${transform_function_arm("Scale", "scale3d", ["number"] * 3)}
-                    ${transform_function_arm("Rotate", "rotate3d", ["number"] * 3 + ["angle"])}
-                    ${transform_function_arm("Perspective", "perspective", ["length"])}
-                    _ => {
-                        // TODO: Convert ComputedOperation::InterpolateMatrix into
-                        //       eCSSKeyword_interpolatematrix, and convert
-                        //       ComputedOperation::AccumulateMatrix into
-                        //       eCSSKeyword_accumulatematrix in the patch series.
-                        gecko_value.mUnit = structs::nsCSSUnit::eCSSUnit_None;
-                    }
-                }
-                cur = (*cur).mNext;
+        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);
             }
         }
-        debug_assert!(iter.next().is_none());
         unsafe { output.set_move(list) };
     }
 
     pub fn set_transform(&mut self, other: longhands::transform::computed_value::T) {
         let vec = if let Some(v) = other.0 {
             v
         } else {
             unsafe {
@@ -2360,17 +2359,17 @@ fn static_assert() {
             # %s is substituted with the call to GetArrayItem.
             css_value_getters = {
                 "length" : "Au(bindings::Gecko_CSSValue_GetAbsoluteLength(%s))",
                 "lop" : "%s.get_lop()",
                 "angle" : "%s.get_angle()",
                 "number" : "bindings::Gecko_CSSValue_GetNumber(%s)",
             }
         %>
-        eCSSKeyword_${keyword} => {
+        structs::nsCSSKeyword::eCSSKeyword_${keyword} => {
             ComputedOperation::${name}(
             % if keyword == "matrix3d":
                 ComputedMatrix {
             % endif
             % for index, item in enumerate(items):
                 % if keyword == "matrix3d":
                     m${index / 4 + 1}${index % 4 + 1}:
                 % endif
@@ -2379,49 +2378,50 @@ fn static_assert() {
                 )},
             % endfor
             % if keyword == "matrix3d":
                 }
             % 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;
+    fn clone_single_transform_function(gecko_value: &structs::nsCSSValue)
+                                       -> longhands::transform::computed_value::ComputedOperation {
         use properties::longhands::transform::computed_value::ComputedMatrix;
         use properties::longhands::transform::computed_value::ComputedOperation;
 
+        let transform_function = unsafe {
+            bindings::Gecko_CSSValue_GetKeyword(bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 0))
+        };
+
+        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"),
+            }
+        }
+    }
+    pub fn clone_transform(&self) -> longhands::transform::computed_value::T {
+        use properties::longhands::transform::computed_value;
+
         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))
+        let list = unsafe { (*self.gecko.mSpecifiedTransform.to_safe().get()).mHead.as_ref() };
+        let result = list.map(|list| {
+            list.into_iter()
+                .map(|value| Self::clone_single_transform_function(value))
+                .collect()
+        });
+        computed_value::T(result)
     }
 
     ${impl_transition_time_value('delay', 'Delay')}
     ${impl_transition_time_value('duration', 'Duration')}
     ${impl_transition_timing_function()}
 
     pub fn transition_combined_duration_at(&self, index: usize) -> f32 {
         // https://drafts.csswg.org/css-transitions/#transition-combined-duration