Bug 1434802: Tweak specified font family serialization in Gecko so that it is simpler. r?xidorn draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Wed, 07 Feb 2018 18:20:23 +0100
changeset 752171 1354c951ad871c9a082c02844986c0dbf6baf84a
parent 752170 d3b80fcda648370b43dd0eb5abf2a70d95deaa73
push id98185
push userbmo:emilio@crisal.io
push dateWed, 07 Feb 2018 17:26:10 +0000
reviewersxidorn
bugs1434802
milestone60.0a1
Bug 1434802: Tweak specified font family serialization in Gecko so that it is simpler. r?xidorn In particular, every time that there's at least more than one identifier, switch to quoted family name, since the reconstruction of the serialization will be lossy anyway. This allows us to avoid copies and all that. What Chrome implements doesn't make much sense in the sense that they always serialize: font-family: "foo"; -> font-family: foo; font-family: foo bar; -> font-family: "foo bar"; font-family: foo\ bar; -> font-family: "foo bar"; This patch makes us match on the second case, but not on the rest, because I think Gecko's behavior is preferable in those cases. MozReview-Commit-ID: JwBECA93lfi
servo/components/style/values/computed/font.rs
--- a/servo/components/style/values/computed/font.rs
+++ b/servo/components/style/values/computed/font.rs
@@ -9,16 +9,17 @@ use app_units::Au;
 use byteorder::{BigEndian, ByteOrder};
 use cssparser::{CssStringWriter, Parser, serialize_identifier};
 #[cfg(feature = "gecko")]
 use gecko_bindings::{bindings, structs};
 #[cfg(feature = "gecko")]
 use gecko_bindings::sugar::refptr::RefPtr;
 #[cfg(feature = "gecko")]
 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
+use std::borrow::Cow;
 use std::fmt::{self, Write};
 #[cfg(feature = "gecko")]
 use std::hash::{Hash, Hasher};
 #[cfg(feature = "servo")]
 use std::slice;
 use style_traits::{CssWriter, ParseError, ToCss};
 use values::CSSFloat;
 use values::animated::{ToAnimatedValue, ToAnimatedZero};
@@ -281,38 +282,35 @@ pub struct FamilyName {
 impl ToCss for FamilyName {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
         match self.syntax {
             FamilyNameSyntax::Quoted => {
                 dest.write_char('"')?;
                 write!(CssStringWriter::new(dest), "{}", self.name)?;
                 dest.write_char('"')
             }
-            FamilyNameSyntax::Identifiers(ref serialization) => {
-                // Note that `serialization` is already escaped/
-                // serialized appropriately.
-                dest.write_str(&*serialization)
+            FamilyNameSyntax::Identifier => {
+                serialize_identifier(&self.name.to_string(), dest)
             }
         }
     }
 }
 
 #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 /// Font family names must either be given quoted as strings,
 /// or unquoted as a sequence of one or more identifiers.
 pub enum FamilyNameSyntax {
     /// The family name was specified in a quoted form, e.g. "Font Name"
-    /// or 'Font Name'.
+    /// or 'Font Name', or as a list of unquoted identifiers.
     Quoted,
 
-    /// The family name was specified in an unquoted form as a sequence of
-    /// identifiers.  The `String` is the serialization of the sequence of
-    /// identifiers.
-    Identifiers(String),
+    /// The family name was specified in an unquoted form as a single
+    /// identifier.
+    Identifier,
 }
 
 #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 /// A set of faces that vary in weight, width or slope.
 pub enum SingleFontFamily {
     /// The name of a font family of choice.
     FamilyName(FamilyName),
@@ -400,38 +398,42 @@ impl SingleFontFamily {
             //  UAs must not consider these keywords as matching the <family-name> type."
             "inherit" => css_wide_keyword = true,
             "initial" => css_wide_keyword = true,
             "unset" => css_wide_keyword = true,
             "default" => css_wide_keyword = true,
             _ => {}
         }
 
-        let mut value = first_ident.as_ref().to_owned();
-        let mut serialization = String::new();
-        serialize_identifier(&first_ident, &mut serialization).unwrap();
+        let mut syntax = FamilyNameSyntax::Identifier;
+        let mut value = Cow::Borrowed(first_ident.as_ref());
 
         // These keywords are not allowed by themselves.
         // The only way this value can be valid with with another keyword.
         if css_wide_keyword {
+            syntax = FamilyNameSyntax::Quoted;
+
             let ident = input.expect_ident()?;
+
+            let value = value.to_mut();
             value.push(' ');
             value.push_str(&ident);
-            serialization.push(' ');
-            serialize_identifier(&ident, &mut serialization).unwrap();
         }
+
         while let Ok(ident) = input.try(|i| i.expect_ident_cloned()) {
+            syntax = FamilyNameSyntax::Quoted;
+
+            let value = value.to_mut();
             value.push(' ');
             value.push_str(&ident);
-            serialization.push(' ');
-            serialize_identifier(&ident, &mut serialization).unwrap();
         }
+
         Ok(SingleFontFamily::FamilyName(FamilyName {
             name: Atom::from(value),
-            syntax: FamilyNameSyntax::Identifiers(serialization),
+            syntax,
         }))
     }
 
     #[cfg(feature = "gecko")]
     /// Return the generic ID for a given generic font name
     pub fn generic(name: &Atom) -> (structs::FontFamilyType, u8) {
         use gecko_bindings::structs::FontFamilyType;
         if *name == atom!("serif") {
@@ -466,21 +468,19 @@ impl SingleFontFamily {
             FontFamilyType::eFamily_sans_serif => SingleFontFamily::Generic(atom!("sans-serif")),
             FontFamilyType::eFamily_serif => SingleFontFamily::Generic(atom!("serif")),
             FontFamilyType::eFamily_monospace => SingleFontFamily::Generic(atom!("monospace")),
             FontFamilyType::eFamily_cursive => SingleFontFamily::Generic(atom!("cursive")),
             FontFamilyType::eFamily_fantasy => SingleFontFamily::Generic(atom!("fantasy")),
             FontFamilyType::eFamily_moz_fixed => SingleFontFamily::Generic(Atom::from("-moz-fixed")),
             FontFamilyType::eFamily_named => {
                 let name = Atom::from(&*family.mName);
-                let mut serialization = String::new();
-                serialize_identifier(&name.to_string(), &mut serialization).unwrap();
                 SingleFontFamily::FamilyName(FamilyName {
                     name: name.clone(),
-                    syntax: FamilyNameSyntax::Identifiers(serialization),
+                    syntax: FamilyNameSyntax::Identifier,
                 })
             },
             FontFamilyType::eFamily_named_quoted => SingleFontFamily::FamilyName(FamilyName {
                 name: (&*family.mName).into(),
                 syntax: FamilyNameSyntax::Quoted,
             }),
             _ => panic!("Found unexpected font FontFamilyType"),
         }
@@ -616,17 +616,17 @@ impl FontFamilyList {
         }
         None
     }
 }
 
 #[cfg(feature = "gecko")]
 /// Iterator of FontFamily
 pub struct FontFamilyNameIter<'a> {
-    names: &'a structs::nsTArray<structs::FontFamilyName>,
+    names: &'a [structs::FontFamilyName],
     cur: usize,
 }
 
 #[cfg(feature = "gecko")]
 impl<'a> Iterator for FontFamilyNameIter<'a> {
     type Item = SingleFontFamily;
 
     fn next(&mut self) -> Option<Self::Item> {