Bug 1382136 - Part 1: Implement clone_content method. r?hiro draft
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Mon, 14 Aug 2017 16:29:57 +0900
changeset 645762 4b2c7f2616438fc4051e83561380ef6fc30e22a0
parent 645674 3bfcbdf5c6c381d5a8febb5c209e27a69fb89f9b
child 645763 4e71f6d23bef137e74aee8e3474a03c5335a132d
push id73879
push userbmo:dakatsuka@mozilla.com
push dateMon, 14 Aug 2017 07:43:37 +0000
reviewershiro
bugs1382136
milestone57.0a1
Bug 1382136 - Part 1: Implement clone_content method. r?hiro MozReview-Commit-ID: 4FpCQ6u5nMe
servo/components/style/gecko/conversions.rs
servo/components/style/gecko/media_queries.rs
servo/components/style/properties/gecko.mako.rs
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -914,8 +914,21 @@ impl<T> Rect<T> where T: GeckoStyleCoord
                 T::from_gecko_style_coord(&sides.data_at(0)).expect("coord[0] cound not convert"),
                 T::from_gecko_style_coord(&sides.data_at(1)).expect("coord[1] cound not convert"),
                 T::from_gecko_style_coord(&sides.data_at(2)).expect("coord[2] cound not convert"),
                 T::from_gecko_style_coord(&sides.data_at(3)).expect("coord[3] cound not convert")
             )
         )
     }
 }
+
+/// Convert to String from given chars pointer.
+pub unsafe fn string_from_chars_pointer(p: *const u16) -> String {
+    use std::slice;
+    let mut length = 0;
+    let mut iter = p;
+    while *iter != 0 {
+        length += 1;
+        iter = iter.offset(1);
+    }
+    let char_vec = slice::from_raw_parts(p, length as usize);
+    String::from_utf16_lossy(char_vec)
+}
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -9,17 +9,17 @@ use app_units::Au;
 use context::QuirksMode;
 use cssparser::{CssStringWriter, Parser, RGBA, Token, BasicParseError};
 use euclid::ScaleFactor;
 use euclid::Size2D;
 use font_metrics::get_metrics_provider_for_product;
 use gecko::values::convert_nscolor_to_rgba;
 use gecko_bindings::bindings;
 use gecko_bindings::structs;
-use gecko_bindings::structs::{nsCSSKeyword, nsCSSProps_KTableEntry, nsCSSValue, nsCSSUnit, nsStringBuffer};
+use gecko_bindings::structs::{nsCSSKeyword, nsCSSProps_KTableEntry, nsCSSValue, nsCSSUnit};
 use gecko_bindings::structs::{nsMediaExpression_Range, nsMediaFeature};
 use gecko_bindings::structs::{nsMediaFeature_ValueType, nsMediaFeature_RangeType, nsMediaFeature_RequirementFlags};
 use gecko_bindings::structs::{nsPresContext, RawGeckoPresContextOwned};
 use gecko_bindings::structs::nsIAtom;
 use media_queries::MediaType;
 use parser::ParserContext;
 use properties::{ComputedValues, StyleBuilder};
 use properties::longhands::font_size;
@@ -289,29 +289,16 @@ impl ToCss for Resolution {
         match *self {
             Resolution::Dpi(v) => write!(dest, "{}dpi", v),
             Resolution::Dppx(v) => write!(dest, "{}dppx", v),
             Resolution::Dpcm(v) => write!(dest, "{}dpcm", v),
         }
     }
 }
 
-unsafe fn string_from_ns_string_buffer(buffer: *const nsStringBuffer) -> String {
-    use std::slice;
-    debug_assert!(!buffer.is_null());
-    let data = buffer.offset(1) as *const u16;
-    let mut length = 0;
-    let mut iter = data;
-    while *iter != 0 {
-        length += 1;
-        iter = iter.offset(1);
-    }
-    String::from_utf16_lossy(slice::from_raw_parts(data, length))
-}
-
 /// A value found or expected in a media expression.
 #[derive(PartialEq, Debug, Clone)]
 pub enum MediaExpressionValue {
     /// A length.
     Length(specified::Length),
     /// A (non-negative) integer.
     Integer(u32),
     /// A floating point value.
@@ -329,16 +316,18 @@ pub enum MediaExpressionValue {
     /// An identifier.
     ///
     /// TODO(emilio): Maybe atomize?
     Ident(String),
 }
 
 impl MediaExpressionValue {
     fn from_css_value(for_expr: &Expression, css_value: &nsCSSValue) -> Option<Self> {
+        use gecko::conversions::string_from_chars_pointer;
+
         // NB: If there's a null value, that means that we don't support the
         // feature.
         if css_value.mUnit == nsCSSUnit::eCSSUnit_Null {
             return None;
         }
 
         match for_expr.feature.mValueType {
             nsMediaFeature_ValueType::eLength => {
@@ -367,17 +356,19 @@ impl MediaExpressionValue {
             }
             nsMediaFeature_ValueType::eEnumerated => {
                 let value = css_value.integer_unchecked() as i16;
                 Some(MediaExpressionValue::Enumerated(value))
             }
             nsMediaFeature_ValueType::eIdent => {
                 debug_assert!(css_value.mUnit == nsCSSUnit::eCSSUnit_Ident);
                 let string = unsafe {
-                    string_from_ns_string_buffer(*css_value.mValue.mString.as_ref())
+                    let buffer = *css_value.mValue.mString.as_ref();
+                    debug_assert!(!buffer.is_null());
+                    string_from_chars_pointer(buffer.offset(1) as *const u16)
                 };
                 Some(MediaExpressionValue::Ident(string))
             }
             nsMediaFeature_ValueType::eIntRatio => {
                 let array = unsafe { css_value.array_unchecked() };
                 debug_assert_eq!(array.len(), 2);
                 let first = array[0].integer_unchecked();
                 let second = array[1].integer_unchecked();
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -5594,16 +5594,90 @@ clip-path
             Gecko_CopyStyleContentsFrom(&mut self.gecko, &other.gecko)
         }
     }
 
     pub fn reset_content(&mut self, other: &Self) {
         self.copy_content_from(other)
     }
 
+    pub fn clone_content(&self) -> longhands::content::computed_value::T {
+        use gecko::conversions::string_from_chars_pointer;
+        use gecko_bindings::structs::nsStyleContentType::*;
+        use properties::longhands::content::computed_value::{T, ContentItem};
+        use values::generics::CounterStyleOrNone;
+        use values::specified::url::SpecifiedUrl;
+        use values::specified::Attr;
+
+        if self.gecko.mContents.is_empty() {
+            return T::Normal;
+        }
+
+        if self.gecko.mContents.len() == 1 &&
+           self.gecko.mContents[0].mType == eStyleContentType_AltContent {
+            return T::MozAltContent;
+        }
+
+        T::Items(
+            self.gecko.mContents.iter().map(|gecko_content| {
+                match gecko_content.mType {
+                    eStyleContentType_OpenQuote => ContentItem::OpenQuote,
+                    eStyleContentType_CloseQuote => ContentItem::CloseQuote,
+                    eStyleContentType_NoOpenQuote => ContentItem::NoOpenQuote,
+                    eStyleContentType_NoCloseQuote => ContentItem::NoCloseQuote,
+                    eStyleContentType_String => {
+                        let gecko_chars = unsafe { gecko_content.mContent.mString.as_ref() };
+                        let string = unsafe { string_from_chars_pointer(*gecko_chars) };
+                        ContentItem::String(string)
+                    },
+                    eStyleContentType_Attr => {
+                        let gecko_chars = unsafe { gecko_content.mContent.mString.as_ref() };
+                        let string = unsafe { string_from_chars_pointer(*gecko_chars) };
+                        let (namespace, attribute) =
+                            match string.find('|') {
+                                None => (None, string),
+                                Some(index) => {
+                                    let (_, val) = string.split_at(index);
+                                    // FIXME: We should give NamespaceId as well to make Attr
+                                    // struct. However, there is no field for it in Gecko.
+                                    debug_assert!(false, "Attr with namespace does not support yet");
+                                    (None, val.to_string())
+                                }
+                            };
+                        ContentItem::Attr(Attr { namespace, attribute })
+                    },
+                    eStyleContentType_Counter | eStyleContentType_Counters => {
+                        let gecko_function =
+                            unsafe { &**gecko_content.mContent.mCounters.as_ref() };
+                        let ident = gecko_function.mIdent.to_string();
+                        let style =
+                            CounterStyleOrNone::from_gecko_value(&gecko_function.mCounterStyle);
+                        if gecko_content.mType == eStyleContentType_Counter {
+                            ContentItem::Counter(ident, style)
+                        } else {
+                            let separator = gecko_function.mSeparator.to_string();
+                            ContentItem::Counters(ident, separator, style)
+                        }
+                    },
+                    eStyleContentType_Image => {
+                        unsafe {
+                            let gecko_image_request =
+                                unsafe { &**gecko_content.mContent.mImage.as_ref() };
+                            ContentItem::Url(
+                                SpecifiedUrl::from_image_request(gecko_image_request)
+                                    .expect("mContent could not convert to SpecifiedUrl")
+                            )
+                        }
+                    },
+                    x => panic!("Found unexpected value in style struct for content property: {:?}", x),
+                }
+            }).collect()
+        )
+    }
+
     % for counter_property in ["Increment", "Reset"]:
         pub fn set_counter_${counter_property.lower()}(&mut self, v: longhands::counter_increment::computed_value::T) {
             unsafe {
                 bindings::Gecko_ClearAndResizeCounter${counter_property}s(&mut self.gecko,
                                                                       v.0.len() as u32);
                 for (i, (name, value)) in v.0.into_iter().enumerate() {
                     self.gecko.m${counter_property}s[i].mCounter.assign(name.0.as_slice());
                     self.gecko.m${counter_property}s[i].mValue = value;