Bug 1356134 - (wip) stylo: support font-variant shorthand. draft
authorJeremy Chen <jeremychen@mozilla.com>
Sat, 03 Jun 2017 19:55:24 +0800
changeset 590318 ebbe701c84b7cebf8855b56cb84636e2d3dfc928
parent 590317 a49112c7a5765802096b3fc298069b9495436107
child 590319 9863dd0f8d6a01376cedbd428e858323fcc72d9c
push id62699
push userbmo:jeremychen@mozilla.com
push dateWed, 07 Jun 2017 16:10:28 +0000
bugs1356134
milestone55.0a1
Bug 1356134 - (wip) stylo: support font-variant shorthand. TODO: figure out why the following 2 tests can't pass: 'traditional oldstyle-nums' 'traditional historical-forms styleset(ok-alt-a, ok-alt-b)' MozReview-Commit-ID: Ipg8OBGPuEN
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/shorthand/font.mako.rs
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -1563,17 +1563,20 @@ macro_rules! exclusive_value {
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T::empty()
     }
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
         SpecifiedValue::Value(VariantLigatures::empty())
     }
-
+    #[inline]
+    pub fn get_none_specified_value() -> SpecifiedValue {
+        SpecifiedValue::Value(NONE)
+    }
     /// normal | none |
     /// [ <common-lig-values> ||
     ///   <discretionary-lig-values> ||
     ///   <historical-lig-values> ||
     ///   <contextual-alt-values> ]
     /// <common-lig-values>        = [ common-ligatures | no-common-ligatures ]
     /// <discretionary-lig-values> = [ discretionary-ligatures | no-discretionary-ligatures ]
     /// <historical-lig-values>    = [ historical-ligatures | no-historical-ligatures ]
--- a/servo/components/style/properties/shorthand/font.mako.rs
+++ b/servo/components/style/properties/shorthand/font.mako.rs
@@ -226,72 +226,134 @@
 <%helpers:shorthand name="font-variant"
                     sub_properties="font-variant-caps
                                     ${'font-variant-alternates' if product == 'gecko' or data.testing else ''}
                                     ${'font-variant-east-asian' if product == 'gecko' or data.testing else ''}
                                     ${'font-variant-ligatures' if product == 'gecko' or data.testing else ''}
                                     ${'font-variant-numeric' if product == 'gecko' or data.testing else ''}
                                     ${'font-variant-position' if product == 'gecko' or data.testing else ''}"
                     spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-variant">
-    use properties::longhands::font_variant_caps;
     <% gecko_sub_properties = "alternates east_asian ligatures numeric position".split() %>
-    % if product == "gecko" or data.testing:
-        % for prop in gecko_sub_properties:
-            use properties::longhands::font_variant_${prop};
-        % endfor
-    % endif
+    <%
+        sub_properties = ["caps"]
+        if product == "gecko" or data.testing:
+            sub_properties += gecko_sub_properties
+    %>
+
+    % for prop in sub_properties:
+        use properties::longhands::font_variant_${prop};
+    % endfor
 
     pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
         let mut nb_normals = 0;
-        let mut caps = None;
+        let mut nb_nones = 0;
+        let mut nb_non_normals = 0;
+        % for prop in sub_properties:
+            let mut ${prop} = None;
+        % endfor
         loop {
-            // Special-case 'normal' because it is valid in each of
-            // all sub properties.
-            // Leaves the values to None, 'normal' is the initial value for each of them.
             if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
+                // Any non-normal values used along with 'normal' keyword are invalid.
+                if nb_non_normals > 0 {
+                    return Err(())
+                }
+
                 nb_normals += 1;
-                continue;
+                continue
             }
-            if caps.is_none() {
-                if let Ok(value) = input.try(|input| font_variant_caps::parse(context, input)) {
-                    caps = Some(value);
+            if input.try(|input| input.expect_ident_matching("none")).is_ok() {
+                if nb_nones > 0 {
+                    return Err(())
+                }
+
+                nb_nones += 1;
+                continue
+            }
+        % for prop in sub_properties:
+            if ${prop}.is_none() {
+                if let Ok(value) = input.try(|i| font_variant_${prop}::parse(context, i)) {
+                    // 'normal' and 'none' keywords should've been parsed already. Any non-normal
+                    // values used along with them are invalid.
+                    if nb_normals > 0 || nb_nones > 0 {
+                        return Err(())
+                    }
+
+                    ${prop} = Some(value);
+                    nb_non_normals += 1;
                     continue
                 }
             }
+        % endfor
+
             break
         }
-        #[inline]
-        fn count<T>(opt: &Option<T>) -> u8 {
-            if opt.is_some() { 1 } else { 0 }
-        }
-        let count = count(&caps) + nb_normals;
-        if count == 0 || count > 1 {
-            return Err(())
-        }
+
+        % if product == "gecko" or data.testing:
+            let count = nb_normals + nb_nones + nb_non_normals;
+            if count == 0 || count > 6 {
+                return Err(())
+            }
+            if nb_nones == 1 {
+                ligatures = Some(font_variant_ligatures::get_none_specified_value());
+            }
+        % else:
+            let count = nb_normals + nb_non_normals;
+            if count == 0 || count > 1 {
+                return Err(())
+            }
+        % endif
+
         Ok(expanded! {
-            font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),
-            // FIXME: Bug 1356134 - parse all sub properties.
-            % if product == "gecko" or data.testing:
-                % for name in gecko_sub_properties:
-                    font_variant_${name}: font_variant_${name}::get_initial_specified_value(),
-                % endfor
-            % endif
+            % for prop in sub_properties:
+                font_variant_${prop}: unwrap_or_initial!(font_variant_${prop}, ${prop}),
+            % endfor
         })
     }
 
     impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        #[allow(unused_assignments)]
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
 
-    % if product == "gecko" or data.testing:
-        % for name in gecko_sub_properties:
-            // FIXME: Bug 1356134 - handle all sub properties.
-            if self.font_variant_${name} != &font_variant_${name}::get_initial_specified_value() {
-                return Ok(());
+            let mut has_none_ligatures = false;
+        % if product == "gecko" or data.testing:
+            // FIXME: Bug 1356124 - implement font-variant-alternates properly.
+            // if self.font_variant_alternates != &font_variant_alternates::get_initial_specified_value() {
+            //     return Ok(())
+            // }
+            has_none_ligatures = self.font_variant_ligatures == &font_variant_ligatures::get_none_specified_value();
+        % endif
+
+            let mut nb_normals = 0;
+            let mut nb_total = 0;
+        % for prop in sub_properties:
+            if self.font_variant_${prop} == &font_variant_${prop}::get_initial_specified_value() {
+                nb_normals += 1;
             }
+            nb_total += 1;
         % endfor
-    % endif
 
-            self.font_variant_caps.to_css(dest)?;
+            if nb_normals > 0 && nb_normals == nb_total {
+                dest.write_str("normal")?;
+            } else if has_none_ligatures {
+                if nb_normals == nb_total - 1 {
+                    // Serialize to ‘none’ if ‘font-variant-ligatures’ is set to ‘none’ and all other
+                    // font feature properties are reset to their initial value.
+                    dest.write_str("none")?;
+                } else {
+                    return Ok(())
+                }
+            } else {
+                let mut has_any = false;
+            % for prop in sub_properties:
+                if self.font_variant_${prop} != &font_variant_${prop}::get_initial_specified_value() {
+                    if has_any {
+                        dest.write_str(" ")?;
+                    }
+                    has_any = true;
+                    self.font_variant_${prop}.to_css(dest)?;
+                }
+            % endfor
+            }
 
             Ok(())
         }
     }
 </%helpers:shorthand>