Auto merge of #20033 - upsuper:font-space, r=emilio

Output unquoted family name as a series of identifiers

It tries to serialize unquoted family names as a series of identifiers. For family names which contain special white spaces like leading white space, trailing white space, and consective white spaces, unquoted names are marked quoted in parsing to avoid complicating serialization code.

This fixes [bug 1434802](https://bugzilla.mozilla.org/show_bug.cgi?id=1434802).

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/20033)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-02-12 18:21:47 -05:00 committed by GitHub
commit 3d6ce6c36a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -286,10 +286,20 @@ impl ToCss for FamilyName {
write!(CssStringWriter::new(dest), "{}", self.name)?; write!(CssStringWriter::new(dest), "{}", self.name)?;
dest.write_char('"') dest.write_char('"')
} }
FamilyNameSyntax::Identifiers(ref serialization) => { FamilyNameSyntax::Identifiers => {
// Note that `serialization` is already escaped/ let mut first = true;
// serialized appropriately. for ident in self.name.to_string().split(' ') {
dest.write_str(&*serialization) if first {
first = false;
} else {
dest.write_char(' ')?;
}
debug_assert!(!ident.is_empty(), "Family name with leading, \
trailing, or consecutive white spaces should \
have been marked quoted by the parser");
serialize_identifier(ident, dest)?;
}
Ok(())
} }
} }
} }
@ -305,9 +315,8 @@ pub enum FamilyNameSyntax {
Quoted, Quoted,
/// The family name was specified in an unquoted form as a sequence of /// 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.
Identifiers(String), Identifiers,
} }
#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)] #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
@ -406,8 +415,6 @@ impl SingleFontFamily {
} }
let mut value = first_ident.as_ref().to_owned(); let mut value = first_ident.as_ref().to_owned();
let mut serialization = String::new();
serialize_identifier(&first_ident, &mut serialization).unwrap();
// These keywords are not allowed by themselves. // These keywords are not allowed by themselves.
// The only way this value can be valid with with another keyword. // The only way this value can be valid with with another keyword.
@ -415,18 +422,23 @@ impl SingleFontFamily {
let ident = input.expect_ident()?; let ident = input.expect_ident()?;
value.push(' '); value.push(' ');
value.push_str(&ident); value.push_str(&ident);
serialization.push(' ');
serialize_identifier(&ident, &mut serialization).unwrap();
} }
while let Ok(ident) = input.try(|i| i.expect_ident_cloned()) { while let Ok(ident) = input.try(|i| i.expect_ident_cloned()) {
value.push(' '); value.push(' ');
value.push_str(&ident); value.push_str(&ident);
serialization.push(' ');
serialize_identifier(&ident, &mut serialization).unwrap();
} }
let syntax = if value.starts_with(' ') || value.ends_with(' ') || value.contains(" ") {
// For font family names which contains special white spaces, e.g.
// `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them
// as identifiers correctly. Just mark them quoted so we don't need
// to worry about them in serialization code.
FamilyNameSyntax::Quoted
} else {
FamilyNameSyntax::Identifiers
};
Ok(SingleFontFamily::FamilyName(FamilyName { Ok(SingleFontFamily::FamilyName(FamilyName {
name: Atom::from(value), name: Atom::from(value),
syntax: FamilyNameSyntax::Identifiers(serialization), syntax
})) }))
} }
@ -461,7 +473,6 @@ impl SingleFontFamily {
/// Get the corresponding font-family with family name /// Get the corresponding font-family with family name
fn from_font_family_name(family: &structs::FontFamilyName) -> SingleFontFamily { fn from_font_family_name(family: &structs::FontFamilyName) -> SingleFontFamily {
use gecko_bindings::structs::FontFamilyType; use gecko_bindings::structs::FontFamilyType;
use values::serialize_atom_identifier;
match family.mType { match family.mType {
FontFamilyType::eFamily_sans_serif => SingleFontFamily::Generic(atom!("sans-serif")), FontFamilyType::eFamily_sans_serif => SingleFontFamily::Generic(atom!("sans-serif")),
@ -472,11 +483,9 @@ impl SingleFontFamily {
FontFamilyType::eFamily_moz_fixed => SingleFontFamily::Generic(Atom::from("-moz-fixed")), FontFamilyType::eFamily_moz_fixed => SingleFontFamily::Generic(Atom::from("-moz-fixed")),
FontFamilyType::eFamily_named => { FontFamilyType::eFamily_named => {
let name = Atom::from(&*family.mName); let name = Atom::from(&*family.mName);
let mut serialization = String::new();
serialize_atom_identifier(&name, &mut serialization).unwrap();
SingleFontFamily::FamilyName(FamilyName { SingleFontFamily::FamilyName(FamilyName {
name, name,
syntax: FamilyNameSyntax::Identifiers(serialization), syntax: FamilyNameSyntax::Identifiers,
}) })
}, },
FontFamilyType::eFamily_named_quoted => SingleFontFamily::FamilyName(FamilyName { FontFamilyType::eFamily_named_quoted => SingleFontFamily::FamilyName(FamilyName {