diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index a52a7e1f498..d605bda6bb2 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -19,7 +19,7 @@ no_viewport_percentage!(SpecifiedValue); pub mod computed_value { - use cssparser::CssStringWriter; + use cssparser::{CssStringWriter, Parser}; use std::fmt::{self, Write}; use Atom; use style_traits::ToCss; @@ -73,6 +73,53 @@ } FontFamily::FamilyName(FamilyName(input)) } + + /// Parse a font-family value + pub fn parse(input: &mut Parser) -> Result { + if let Ok(value) = input.try(|input| input.expect_string()) { + return Ok(FontFamily::FamilyName(FamilyName(Atom::from(&*value)))) + } + let first_ident = try!(input.expect_ident()); + + // FIXME(bholley): The fast thing to do here would be to look up the + // string (as lowercase) in the static atoms table. We don't have an + // API to do that yet though, so we do the simple thing for now. + let mut css_wide_keyword = false; + match_ignore_ascii_case! { first_ident, + "serif" => return Ok(FontFamily::Generic(atom!("serif"))), + "sans-serif" => return Ok(FontFamily::Generic(atom!("sans-serif"))), + "cursive" => return Ok(FontFamily::Generic(atom!("cursive"))), + "fantasy" => return Ok(FontFamily::Generic(atom!("fantasy"))), + "monospace" => return Ok(FontFamily::Generic(atom!("monospace"))), + + // https://drafts.csswg.org/css-fonts/#propdef-font-family + // "Font family names that happen to be the same as a keyword value + // (`inherit`, `serif`, `sans-serif`, `monospace`, `fantasy`, and `cursive`) + // must be quoted to prevent confusion with the keywords with the same names. + // The keywords ‘initial’ and ‘default’ are reserved for future use + // and must also be quoted when used as font names. + // UAs must not consider these keywords as matching the 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.into_owned(); + // These keywords are not allowed by themselves. + // The only way this value can be valid with with another keyword. + if css_wide_keyword { + let ident = input.expect_ident()?; + value.push_str(" "); + value.push_str(&ident); + } + while let Ok(ident) = input.try(|input| input.expect_ident()) { + value.push_str(" "); + value.push_str(&ident); + } + Ok(FontFamily::FamilyName(FamilyName(Atom::from(value)))) + } } impl ToCss for FamilyName { @@ -119,75 +166,27 @@ /// # /// = | [ + ] /// TODO: - pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { - Vec::::parse(context, input).map(SpecifiedValue) + pub fn parse(_: &ParserContext, input: &mut Parser) -> Result { + SpecifiedValue::parse(input) } - impl Parse for Vec { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - input.parse_comma_separated(|input| FontFamily::parse(context, input)) + impl SpecifiedValue { + pub fn parse(input: &mut Parser) -> Result { + input.parse_comma_separated(|input| FontFamily::parse(input)).map(SpecifiedValue) } } /// `FamilyName::parse` is based on `FontFamily::parse` and not the other way around /// because we want the former to exclude generic family keywords. impl Parse for FamilyName { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - match FontFamily::parse(context, input) { + fn parse(_: &ParserContext, input: &mut Parser) -> Result { + match FontFamily::parse(input) { Ok(FontFamily::FamilyName(name)) => Ok(name), Ok(FontFamily::Generic(_)) | Err(()) => Err(()) } } } - - impl Parse for FontFamily { - fn parse(_context: &ParserContext, input: &mut Parser) -> Result { - if let Ok(value) = input.try(|input| input.expect_string()) { - return Ok(FontFamily::FamilyName(FamilyName(Atom::from(&*value)))) - } - let first_ident = try!(input.expect_ident()); - - // FIXME(bholley): The fast thing to do here would be to look up the - // string (as lowercase) in the static atoms table. We don't have an - // API to do that yet though, so we do the simple thing for now. - let mut css_wide_keyword = false; - match_ignore_ascii_case! { first_ident, - "serif" => return Ok(FontFamily::Generic(atom!("serif"))), - "sans-serif" => return Ok(FontFamily::Generic(atom!("sans-serif"))), - "cursive" => return Ok(FontFamily::Generic(atom!("cursive"))), - "fantasy" => return Ok(FontFamily::Generic(atom!("fantasy"))), - "monospace" => return Ok(FontFamily::Generic(atom!("monospace"))), - - // https://drafts.csswg.org/css-fonts/#propdef-font-family - // "Font family names that happen to be the same as a keyword value - // (‘inherit’, ‘serif’, ‘sans-serif’, ‘monospace’, ‘fantasy’, and ‘cursive’) - // must be quoted to prevent confusion with the keywords with the same names. - // The keywords ‘initial’ and ‘default’ are reserved for future use - // and must also be quoted when used as font names. - // UAs must not consider these keywords as matching the 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.into_owned(); - // These keywords are not allowed by themselves. - // The only way this value can be valid with with another keyword. - if css_wide_keyword { - let ident = input.expect_ident()?; - value.push_str(" "); - value.push_str(&ident); - } - while let Ok(ident) = input.try(|input| input.expect_ident()) { - value.push_str(" "); - value.push_str(&ident); - } - Ok(FontFamily::FamilyName(FamilyName(Atom::from(value)))) - } - } diff --git a/components/style/properties/shorthand/font.mako.rs b/components/style/properties/shorthand/font.mako.rs index 1bcf2c10616..2a89e461ad0 100644 --- a/components/style/properties/shorthand/font.mako.rs +++ b/components/style/properties/shorthand/font.mako.rs @@ -12,10 +12,9 @@ ${'font-variant-position' if product == 'gecko' else ''} ${'font-language-override' if product == 'none' else ''}" spec="https://drafts.csswg.org/css-fonts-3/#propdef-font"> - use parser::Parse; use properties::longhands::{font_style, font_variant, font_weight, font_stretch}; - use properties::longhands::{font_size, line_height, font_family}; - use properties::longhands::font_family::computed_value::FontFamily; + use properties::longhands::{font_size, line_height}; + use properties::longhands::font_family::SpecifiedValue as FontFamily; pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result { let mut nb_normals = 0; @@ -71,7 +70,7 @@ } else { None }; - let family = Vec::::parse(context, input)?; + let family = FontFamily::parse(input)?; Ok(Longhands { font_style: style, font_variant: variant, @@ -79,7 +78,7 @@ font_stretch: stretch, font_size: size, line_height: line_height, - font_family: Some(font_family::SpecifiedValue(family)), + font_family: Some(family), % if product == "gecko": font_size_adjust: None, font_kerning: None, diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 94877a11fa0..88fdeb03b57 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -1150,10 +1150,22 @@ pub extern "C" fn Servo_DeclarationBlock_SetColorValue(declarations: } #[no_mangle] -pub extern "C" fn Servo_DeclarationBlock_SetFontFamily(_: - RawServoDeclarationBlockBorrowed, - _: *const nsAString) { +pub extern "C" fn Servo_DeclarationBlock_SetFontFamily(declarations: + RawServoDeclarationBlockBorrowed, + value: *const nsAString) { + use cssparser::Parser; + use style::properties::{DeclaredValue, PropertyDeclaration}; + use style::properties::longhands::font_family::SpecifiedValue as FontFamily; + let declarations = RwLock::::as_arc(&declarations); + let string = unsafe { (*value).to_string() }; + let mut parser = Parser::new(&string); + if let Ok(family) = FontFamily::parse(&mut parser) { + if parser.is_exhausted() { + let decl = PropertyDeclaration::FontFamily(DeclaredValue::Value(family)); + declarations.write().declarations.push((decl, Default::default())); + } + } } #[no_mangle]