diff --git a/servo/components/gfx/font_cache_thread.rs b/servo/components/gfx/font_cache_thread.rs index c402610369f0..14c58ac411ce 100644 --- a/servo/components/gfx/font_cache_thread.rs +++ b/servo/components/gfx/font_cache_thread.rs @@ -26,7 +26,7 @@ use std::sync::{Arc, Mutex}; use std::thread; use std::u32; use style::font_face::{EffectiveSources, Source}; -use style::properties::longhands::font_family::computed_value::{FontFamily, FamilyName}; +use style::values::computed::font::{SingleFontFamily, FamilyName}; use webrender_api; /// A list of font templates that make up a given font family. @@ -105,7 +105,7 @@ impl FontTemplates { /// Commands that the FontContext sends to the font cache thread. #[derive(Debug, Deserialize, Serialize)] pub enum Command { - GetFontTemplate(FontFamily, FontTemplateDescriptor, IpcSender), + GetFontTemplate(SingleFontFamily, FontTemplateDescriptor, IpcSender), GetLastResortFontTemplate(FontTemplateDescriptor, IpcSender), GetFontInstance(webrender_api::FontKey, Au, IpcSender), AddWebFont(LowercaseString, EffectiveSources, IpcSender<()>), @@ -124,7 +124,7 @@ pub enum Reply { struct FontCache { port: IpcReceiver, channel_to_self: IpcSender, - generic_fonts: HashMap, + generic_fonts: HashMap, local_families: HashMap, web_families: HashMap, font_context: FontContextHandle, @@ -134,17 +134,17 @@ struct FontCache { font_instances: HashMap<(webrender_api::FontKey, Au), webrender_api::FontInstanceKey>, } -fn populate_generic_fonts() -> HashMap { +fn populate_generic_fonts() -> HashMap { let mut generic_fonts = HashMap::with_capacity(5); - append_map(&mut generic_fonts, FontFamily::Generic(atom!("serif")), "Times New Roman"); - append_map(&mut generic_fonts, FontFamily::Generic(atom!("sans-serif")), SANS_SERIF_FONT_FAMILY); - append_map(&mut generic_fonts, FontFamily::Generic(atom!("cursive")), "Apple Chancery"); - append_map(&mut generic_fonts, FontFamily::Generic(atom!("fantasy")), "Papyrus"); - append_map(&mut generic_fonts, FontFamily::Generic(atom!("monospace")), "Menlo"); + append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("serif")), "Times New Roman"); + append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("sans-serif")), SANS_SERIF_FONT_FAMILY); + append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("cursive")), "Apple Chancery"); + append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("fantasy")), "Papyrus"); + append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("monospace")), "Menlo"); - fn append_map(generic_fonts: &mut HashMap, - font_family: FontFamily, + fn append_map(generic_fonts: &mut HashMap, + font_family: SingleFontFamily, mapped_name: &str) { let family_name = { let opt_system_default = system_default_family(font_family.name()); @@ -320,7 +320,7 @@ impl FontCache { }); } - fn transform_family(&self, family: &FontFamily) -> LowercaseString { + fn transform_family(&self, family: &SingleFontFamily) -> LowercaseString { match self.generic_fonts.get(family) { None => LowercaseString::new(family.name()), Some(mapped_family) => (*mapped_family).clone() @@ -351,7 +351,7 @@ impl FontCache { } } - fn find_font_in_web_family(&mut self, family: &FontFamily, desc: &FontTemplateDescriptor) + fn find_font_in_web_family(&mut self, family: &SingleFontFamily, desc: &FontTemplateDescriptor) -> Option> { let family_name = LowercaseString::new(family.name()); @@ -385,7 +385,7 @@ impl FontCache { } } - fn find_font_template(&mut self, family: &FontFamily, desc: &FontTemplateDescriptor) + fn find_font_template(&mut self, family: &SingleFontFamily, desc: &FontTemplateDescriptor) -> Option { let template = self.find_font_in_web_family(family, desc) .or_else(|| { @@ -453,7 +453,7 @@ impl FontCacheThread { } } - pub fn find_font_template(&self, family: FontFamily, desc: FontTemplateDescriptor) + pub fn find_font_template(&self, family: SingleFontFamily, desc: FontTemplateDescriptor) -> Option { let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); diff --git a/servo/components/script/dom/element.rs b/servo/components/script/dom/element.rs index b11498e28503..84021ca7c389 100644 --- a/servo/components/script/dom/element.rs +++ b/servo/components/script/dom/element.rs @@ -587,9 +587,9 @@ impl LayoutElementHelpers for LayoutDom { shared_lock, PropertyDeclaration::FontFamily( font_family::SpecifiedValue::Values( - font_family::computed_value::FontFamilyList::new(vec![ - font_family::computed_value::FontFamily::from_atom( - font_family)]))))); + computed::font::FontFamilyList::new(Box::new([ + computed::font::SingleFontFamily::from_atom( + font_family)])))))); } let font_size = self.downcast::().and_then(|this| this.get_size()); diff --git a/servo/components/style/font_face.rs b/servo/components/style/font_face.rs index e1f76a1d6fef..80c32a552e74 100644 --- a/servo/components/style/font_face.rs +++ b/servo/components/style/font_face.rs @@ -10,7 +10,6 @@ #[cfg(feature = "gecko")] use computed_values::{font_feature_settings, font_stretch, font_style, font_weight}; -use computed_values::font_family::FamilyName; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use cssparser::{SourceLocation, CowRcStr}; use error_reporting::{ContextualParseError, ParseErrorReporter}; @@ -23,6 +22,7 @@ use selectors::parser::SelectorParseErrorKind; use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; use std::fmt; use style_traits::{Comma, OneOrMoreSeparated, ParseError, StyleParseErrorKind, ToCss}; +use values::computed::font::FamilyName; use values::specified::url::SpecifiedUrl; /// A source for a font-face rule. diff --git a/servo/components/style/gecko/rules.rs b/servo/components/style/gecko/rules.rs index 88e9600c841f..9fc73e7fd962 100644 --- a/servo/components/style/gecko/rules.rs +++ b/servo/components/style/gecko/rules.rs @@ -6,7 +6,6 @@ use byteorder::{BigEndian, WriteBytesExt}; use computed_values::{font_feature_settings, font_stretch, font_style, font_weight}; -use computed_values::font_family::FamilyName; use counter_style; use cssparser::UnicodeRange; use font_face::{FontFaceRuleData, Source, FontDisplay, FontWeight}; @@ -19,6 +18,7 @@ use nsstring::nsString; use properties::longhands::font_language_override; use shared_lock::{ToCssWithGuard, SharedRwLockReadGuard}; use std::{fmt, str}; +use values::computed::font::FamilyName; use values::generics::FontSettings; /// A @font-face rule diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs index 42d0d40f19fb..2030a27f503e 100644 --- a/servo/components/style/properties/gecko.mako.rs +++ b/servo/components/style/properties/gecko.mako.rs @@ -59,7 +59,7 @@ use std::mem::{forget, uninitialized, transmute, zeroed}; use std::{cmp, ops, ptr}; use values::{self, Auto, CustomIdent, Either, KeyframesName, None_}; use values::computed::{NonNegativeLength, ToComputedValue, Percentage}; -use values::computed::font::FontSize; +use values::computed::font::{FontSize, SingleFontFamily}; use values::computed::effects::{BoxShadow, Filter, SimpleShadow}; use computed_values::border_style; @@ -2441,7 +2441,7 @@ fn static_assert() { 0 } - pub fn font_family_at(&self, _: usize) -> longhands::font_family::computed_value::FontFamily { + pub fn font_family_at(&self, _: usize) -> SingleFontFamily { unimplemented!() } @@ -2457,9 +2457,7 @@ fn static_assert() { pub fn clone_font_family(&self) -> longhands::font_family::computed_value::T { use gecko_bindings::structs::FontFamilyType; - use properties::longhands::font_family::computed_value; - use properties::longhands::font_family::computed_value::FontFamily; - use properties::longhands::font_family::computed_value::FontFamilyList; + use values::computed::font::{FontFamily, SingleFontFamily, FontFamilyList}; let fontlist = &self.gecko.mFont.fontlist; let shared_fontlist = unsafe { fontlist.mFontlist.mBasePtr.to_safe() }; @@ -2467,16 +2465,16 @@ fn static_assert() { if shared_fontlist.mNames.is_empty() { let default = match fontlist.mDefaultFontType { FontFamilyType::eFamily_serif => { - FontFamily::Generic(atom!("serif")) + SingleFontFamily::Generic(atom!("serif")) } FontFamilyType::eFamily_sans_serif => { - FontFamily::Generic(atom!("sans-serif")) + SingleFontFamily::Generic(atom!("sans-serif")) } _ => panic!("Default generic must be serif or sans-serif"), }; - computed_value::T(FontFamilyList::new(vec![default])) + FontFamily(FontFamilyList::new(Box::new([default]))) } else { - computed_value::T(FontFamilyList(shared_fontlist)) + FontFamily(FontFamilyList(shared_fontlist)) } } diff --git a/servo/components/style/properties/longhand/font.mako.rs b/servo/components/style/properties/longhand/font.mako.rs index 125fee1a932b..046f5cb35930 100644 --- a/servo/components/style/properties/longhand/font.mako.rs +++ b/servo/components/style/properties/longhand/font.mako.rs @@ -7,536 +7,12 @@ <% data.new_style_struct("Font", inherited=True) %> -<%def name="nongecko_unreachable()"> - %if product == "gecko": - ${caller.body()} - %else: - unreachable!() - %endif - - -<%helpers:longhand name="font-family" animation_value_type="discrete" - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" - spec="https://drafts.csswg.org/css-fonts/#propdef-font-family"> - #[cfg(feature = "gecko")] use gecko_bindings::bindings; - #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; - use properties::longhands::system_font::SystemFont; - use self::computed_value::{FontFamily, FontFamilyList, FamilyName}; - use std::fmt; - use style_traits::ToCss; - - - pub mod computed_value { - 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::fmt::{self, Write}; - #[cfg(feature = "gecko")] use std::hash::{Hash, Hasher}; - #[cfg(feature = "servo")] use std::slice; - use style_traits::{ToCss, ParseError}; - use Atom; - pub use self::FontFamily as SingleComputedValue; - - #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)] - #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] - pub enum FontFamily { - FamilyName(FamilyName), - Generic(Atom), - } - - #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)] - #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] - pub struct FamilyName { - pub name: Atom, - pub syntax: FamilyNameSyntax, - } - - #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)] - #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] - pub enum FamilyNameSyntax { - /// The family name was specified in a quoted form, e.g. "Font Name" - /// or 'Font Name'. - 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), - } - - impl FontFamily { - #[inline] - pub fn atom(&self) -> &Atom { - match *self { - FontFamily::FamilyName(ref family_name) => &family_name.name, - FontFamily::Generic(ref name) => name, - } - } - - #[inline] - #[cfg(not(feature = "gecko"))] // Gecko can't borrow atoms as UTF-8. - pub fn name(&self) -> &str { - self.atom() - } - - #[cfg(not(feature = "gecko"))] // Gecko can't borrow atoms as UTF-8. - pub fn from_atom(input: Atom) -> FontFamily { - match input { - atom!("serif") | - atom!("sans-serif") | - atom!("cursive") | - atom!("fantasy") | - atom!("monospace") => { - return FontFamily::Generic(input) - } - _ => {} - } - match_ignore_ascii_case! { &input, - "serif" => return FontFamily::Generic(atom!("serif")), - "sans-serif" => return FontFamily::Generic(atom!("sans-serif")), - "cursive" => return FontFamily::Generic(atom!("cursive")), - "fantasy" => return FontFamily::Generic(atom!("fantasy")), - "monospace" => return FontFamily::Generic(atom!("monospace")), - _ => {} - } - - // We don't know if it's quoted or not. So we set it to - // quoted by default. - FontFamily::FamilyName(FamilyName { - name: input, - syntax: FamilyNameSyntax::Quoted, - }) - } - - /// Parse a font-family value - pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { - if let Ok(value) = input.try(|i| i.expect_string_cloned()) { - return Ok(FontFamily::FamilyName(FamilyName { - name: Atom::from(&*value), - syntax: FamilyNameSyntax::Quoted, - })) - } - let first_ident = input.expect_ident()?.clone(); - - // 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"))), - % if product == "gecko": - "-moz-fixed" => return Ok(FontFamily::Generic(atom!("-moz-fixed"))), - % endif - - // 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.as_ref().to_owned(); - let mut serialization = String::new(); - serialize_identifier(&first_ident, &mut serialization).unwrap(); - - // 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(' '); - value.push_str(&ident); - serialization.push(' '); - serialize_identifier(&ident, &mut serialization).unwrap(); - } - while let Ok(ident) = input.try(|i| i.expect_ident_cloned()) { - value.push(' '); - value.push_str(&ident); - serialization.push(' '); - serialize_identifier(&ident, &mut serialization).unwrap(); - } - Ok(FontFamily::FamilyName(FamilyName { - name: Atom::from(value), - syntax: FamilyNameSyntax::Identifiers(serialization), - })) - } - - #[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") { - (FontFamilyType::eFamily_serif, - structs::kGenericFont_serif) - } else if *name == atom!("sans-serif") { - (FontFamilyType::eFamily_sans_serif, - structs::kGenericFont_sans_serif) - } else if *name == atom!("cursive") { - (FontFamilyType::eFamily_cursive, - structs::kGenericFont_cursive) - } else if *name == atom!("fantasy") { - (FontFamilyType::eFamily_fantasy, - structs::kGenericFont_fantasy) - } else if *name == atom!("monospace") { - (FontFamilyType::eFamily_monospace, - structs::kGenericFont_monospace) - } else if *name == atom!("-moz-fixed") { - (FontFamilyType::eFamily_moz_fixed, - structs::kGenericFont_moz_fixed) - } else { - panic!("Unknown generic {}", name); - } - } - - #[cfg(feature = "gecko")] - fn from_font_family_name(family: &structs::FontFamilyName) -> FontFamily { - use gecko_bindings::structs::FontFamilyType; - - match family.mType { - FontFamilyType::eFamily_serif => FontFamily::Generic(atom!("serif")), - FontFamilyType::eFamily_sans_serif => FontFamily::Generic(atom!("sans-serif")), - FontFamilyType::eFamily_monospace => FontFamily::Generic(atom!("monospace")), - FontFamilyType::eFamily_cursive => FontFamily::Generic(atom!("cursive")), - FontFamilyType::eFamily_fantasy => FontFamily::Generic(atom!("fantasy")), - FontFamilyType::eFamily_moz_fixed => FontFamily::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(); - FontFamily::FamilyName(FamilyName { - name: name.clone(), - syntax: FamilyNameSyntax::Identifiers(serialization), - }) - }, - FontFamilyType::eFamily_named_quoted => FontFamily::FamilyName(FamilyName { - name: (&*family.mName).into(), - syntax: FamilyNameSyntax::Quoted, - }), - x => panic!("Found unexpected font FontFamilyType: {:?}", x), - } - } - } - - impl ToCss for FamilyName { - fn to_css(&self, dest: &mut 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) - } - } - } - } - - impl ToCss for FontFamily { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - FontFamily::FamilyName(ref name) => name.to_css(dest), - - // All generic values accepted by the parser are known to not require escaping. - FontFamily::Generic(ref name) => { - % if product == "gecko": - // We should treat -moz-fixed as monospace - if name == &atom!("-moz-fixed") { - return dest.write_str("monospace"); - } - % endif - - write!(dest, "{}", name) - }, - } - } - } - - impl ToCss for T { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - let mut iter = self.0.iter(); - iter.next().unwrap().to_css(dest)?; - for family in iter { - dest.write_str(", ")?; - family.to_css(dest)?; - } - Ok(()) - } - } - - #[cfg(feature = "servo")] - #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)] - pub struct FontFamilyList(Vec); - - #[cfg(feature = "gecko")] - #[derive(Clone, Debug)] - pub struct FontFamilyList(pub RefPtr); - - #[cfg(feature = "gecko")] - impl Hash for FontFamilyList { - fn hash(&self, state: &mut H) where H: Hasher { - for name in self.0.mNames.iter() { - name.mType.hash(state); - name.mName.hash(state); - } - } - } - - #[cfg(feature = "gecko")] - impl PartialEq for FontFamilyList { - fn eq(&self, other: &FontFamilyList) -> bool { - if self.0.mNames.len() != other.0.mNames.len() { - return false; - } - for (a, b) in self.0.mNames.iter().zip(other.0.mNames.iter()) { - if a.mType != b.mType || &*a.mName != &*b.mName { - return false; - } - } - true - } - } - - #[cfg(feature = "gecko")] - impl Eq for FontFamilyList {} - - impl FontFamilyList { - #[cfg(feature = "servo")] - pub fn new(families: Vec) -> FontFamilyList { - FontFamilyList(families) - } - - #[cfg(feature = "gecko")] - pub fn new(families: Vec) -> FontFamilyList { - let fontlist; - let names; - unsafe { - fontlist = bindings::Gecko_SharedFontList_Create(); - names = &mut (*fontlist).mNames; - names.ensure_capacity(families.len()); - }; - - for family in families { - match family { - FontFamily::FamilyName(ref f) => { - let quoted = matches!(f.syntax, FamilyNameSyntax::Quoted); - unsafe { - bindings::Gecko_nsTArray_FontFamilyName_AppendNamed( - names, - f.name.as_ptr(), - quoted - ); - } - } - FontFamily::Generic(ref name) => { - let (family_type, _generic) = FontFamily::generic(name); - unsafe { - bindings::Gecko_nsTArray_FontFamilyName_AppendGeneric( - names, - family_type - ); - } - } - } - } - - FontFamilyList(unsafe { RefPtr::from_addrefed(fontlist) }) - } - - #[cfg(feature = "servo")] - pub fn iter(&self) -> slice::Iter { - self.0.iter() - } - - #[cfg(feature = "gecko")] - pub fn iter(&self) -> FontFamilyNameIter { - FontFamilyNameIter { - names: &self.0.mNames, - cur: 0, - } - } - - #[cfg(feature = "gecko")] - /// Return the generic ID if it is a single generic font - pub fn single_generic(&self) -> Option { - let mut iter = self.iter(); - if let Some(FontFamily::Generic(ref name)) = iter.next() { - if iter.next().is_none() { - return Some(FontFamily::generic(name).1); - } - } - None - } - } - - #[cfg(feature = "gecko")] - pub struct FontFamilyNameIter<'a> { - names: &'a structs::nsTArray, - cur: usize, - } - - #[cfg(feature = "gecko")] - impl<'a> Iterator for FontFamilyNameIter<'a> { - type Item = FontFamily; - - fn next(&mut self) -> Option { - if self.cur < self.names.len() { - let item = FontFamily::from_font_family_name(&self.names[self.cur]); - self.cur += 1; - Some(item) - } else { - None - } - } - } - - #[derive(Clone, Debug, Eq, Hash, PartialEq)] - #[cfg_attr(feature = "servo", derive(MallocSizeOf))] - pub struct T(pub FontFamilyList); - - #[cfg(feature = "gecko")] - impl MallocSizeOf for T { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - // SharedFontList objects are generally shared from the pointer - // stored in the specified value. So only count this if the - // SharedFontList is unshared. - unsafe { - bindings::Gecko_SharedFontList_SizeOfIncludingThisIfUnshared( - (self.0).0.get() - ) - } - } - } - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T( - FontFamilyList::new(vec![FontFamily::Generic(atom!("serif"))]) - ) - } - - /// # - /// = | [ + ] - /// TODO: - pub fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) - -> Result> { - SpecifiedValue::parse(input) - } - - #[derive(Clone, Debug, Eq, Hash, PartialEq)] - pub enum SpecifiedValue { - Values(FontFamilyList), - System(SystemFont), - } - - #[cfg(feature = "gecko")] - impl SpecifiedValue { - /// Return the generic ID if it is a single generic font - pub fn single_generic(&self) -> Option { - match *self { - SpecifiedValue::Values(ref values) => values.single_generic(), - _ => None, - } - } - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - fn to_computed_value(&self, _cx: &Context) -> Self::ComputedValue { - match *self { - SpecifiedValue::Values(ref v) => computed_value::T(v.clone()), - SpecifiedValue::System(_) => { - <%self:nongecko_unreachable> - _cx.cached_system_font.as_ref().unwrap().font_family.clone() - - } - } - } - fn from_computed_value(other: &computed_value::T) -> Self { - SpecifiedValue::Values(other.0.clone()) - } - } - - #[cfg(feature = "gecko")] - impl MallocSizeOf for SpecifiedValue { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - match *self { - SpecifiedValue::Values(ref v) => { - // Although a SharedFontList object is refcounted, we always - // attribute its size to the specified value. - unsafe { - bindings::Gecko_SharedFontList_SizeOfIncludingThis( - v.0.get() - ) - } - } - SpecifiedValue::System(_) => 0, - } - } - } - - impl SpecifiedValue { - pub fn system_font(f: SystemFont) -> Self { - SpecifiedValue::System(f) - } - pub fn get_system(&self) -> Option { - if let SpecifiedValue::System(s) = *self { - Some(s) - } else { - None - } - } - - pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { - input.parse_comma_separated(|input| FontFamily::parse(input)).map(|v| { - SpecifiedValue::Values(FontFamilyList::new(v)) - }) - } - } - - impl ToCss for SpecifiedValue { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - SpecifiedValue::Values(ref v) => { - let mut iter = v.iter(); - iter.next().unwrap().to_css(dest)?; - for family in iter { - dest.write_str(", ")?; - family.to_css(dest)?; - } - Ok(()) - } - SpecifiedValue::System(sys) => sys.to_css(dest), - } - } - } - - /// `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<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { - match FontFamily::parse(input) { - Ok(FontFamily::FamilyName(name)) => Ok(name), - Ok(FontFamily::Generic(_)) => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), - Err(e) => Err(e) - } - } - } - +${helpers.predefined_type("font-family", + "FontFamily", + initial_value="computed::FontFamily::serif()", + animation_value_type="discrete", + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", + spec="https://drafts.csswg.org/css-fonts/#propdef-font-family")} ${helpers.single_keyword_system("font-style", "normal italic oblique", @@ -833,7 +309,7 @@ ${helpers.predefined_type("-x-text-zoom", use gecko_bindings::bindings; use gecko_bindings::structs::{LookAndFeel_FontID, nsFont}; use std::mem; - use values::computed::font::FontSize; + use values::computed::font::{FontSize, FontFamilyList}; let id = match *self { % for font in system_fonts: @@ -855,7 +331,7 @@ ${helpers.predefined_type("-x-text-zoom", let weight = longhands::font_weight::computed_value::T::from_gecko_weight(system.weight); let ret = ComputedSystemFont { font_family: longhands::font_family::computed_value::T( - longhands::font_family::computed_value::FontFamilyList( + FontFamilyList( unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() } ) ), diff --git a/servo/components/style/properties/shorthand/font.mako.rs b/servo/components/style/properties/shorthand/font.mako.rs index 716dad9dce0e..b40505e3c860 100644 --- a/servo/components/style/properties/shorthand/font.mako.rs +++ b/servo/components/style/properties/shorthand/font.mako.rs @@ -120,7 +120,7 @@ return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } - let family = FontFamily::parse(input)?; + let family = FontFamily::parse_specified(input)?; Ok(expanded! { % for name in "style weight stretch variant_caps".split(): font_${name}: unwrap_or_initial!(font_${name}, ${name}), diff --git a/servo/components/style/stylesheets/font_feature_values_rule.rs b/servo/components/style/stylesheets/font_feature_values_rule.rs index 932ac08e9b38..d2e6f7c87a14 100644 --- a/servo/components/style/stylesheets/font_feature_values_rule.rs +++ b/servo/components/style/stylesheets/font_feature_values_rule.rs @@ -7,7 +7,6 @@ //! [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule use Atom; -use computed_values::font_family::FamilyName; use cssparser::{AtRuleParser, AtRuleType, BasicParseErrorKind, DeclarationListParser, DeclarationParser, Parser}; use cssparser::{CowRcStr, RuleListParser, SourceLocation, QualifiedRuleParser, Token, serialize_identifier}; use error_reporting::{ContextualParseError, ParseErrorReporter}; @@ -20,6 +19,7 @@ use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; use std::fmt; use style_traits::{ParseError, StyleParseErrorKind, ToCss}; use stylesheets::CssRuleType; +use values::computed::font::FamilyName; /// A @font-feature-values block declaration. /// It is `: +`. diff --git a/servo/components/style/stylesheets/rule_parser.rs b/servo/components/style/stylesheets/rule_parser.rs index 7437e76f701f..c33d1063fcb7 100644 --- a/servo/components/style/stylesheets/rule_parser.rs +++ b/servo/components/style/stylesheets/rule_parser.rs @@ -5,7 +5,6 @@ //! Parsing of the stylesheet contents. use {Namespace, Prefix}; -use computed_values::font_family::FamilyName; use counter_style::{parse_counter_style_body, parse_counter_style_name}; use cssparser::{AtRuleParser, AtRuleType, Parser, QualifiedRuleParser, RuleListParser}; use cssparser::{CowRcStr, SourceLocation, BasicParseError, BasicParseErrorKind}; @@ -31,6 +30,7 @@ use stylesheets::supports_rule::SupportsCondition; use stylesheets::viewport_rule; use values::CustomIdent; use values::KeyframesName; +use values::computed::font::FamilyName; use values::specified::url::SpecifiedUrl; /// The parser for the top-level rules in a stylesheet. diff --git a/servo/components/style/values/computed/font.rs b/servo/components/style/values/computed/font.rs index b013a8187a7c..89cde164f6f8 100644 --- a/servo/components/style/values/computed/font.rs +++ b/servo/components/style/values/computed/font.rs @@ -4,10 +4,22 @@ //! Computed values for font properties +use Atom; use app_units::Au; use byteorder::{BigEndian, ByteOrder}; -use std::fmt; -use style_traits::ToCss; +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::fmt::{self, Write}; +#[cfg(feature = "gecko")] +use std::hash::{Hash, Hasher}; +#[cfg(feature = "servo")] +use std::slice; +use style_traits::{ToCss, ParseError}; use values::CSSFloat; use values::animated::{ToAnimatedValue, ToAnimatedZero}; use values::computed::{Context, NonNegativeLength, ToComputedValue}; @@ -215,6 +227,419 @@ impl ToAnimatedValue for FontSize { } } +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "servo", derive(MallocSizeOf))] +/// Specifies a prioritized list of font family names or generic family names. +pub struct FontFamily(pub FontFamilyList); + +impl FontFamily { + #[inline] + /// Get default font family as `serif` which is a generic font-family + pub fn serif() -> Self { + FontFamily( + FontFamilyList::new(Box::new([SingleFontFamily::Generic(atom!("serif"))])) + ) + } +} + +#[cfg(feature = "gecko")] +impl MallocSizeOf for FontFamily { + fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { + // SharedFontList objects are generally shared from the pointer + // stored in the specified value. So only count this if the + // SharedFontList is unshared. + unsafe { + bindings::Gecko_SharedFontList_SizeOfIncludingThisIfUnshared( + (self.0).0.get() + ) + } + } +} + +impl ToCss for FontFamily { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let mut iter = self.0.iter(); + iter.next().unwrap().to_css(dest)?; + for family in iter { + dest.write_str(", ")?; + family.to_css(dest)?; + } + Ok(()) + } +} + +#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)] +#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +/// The name of a font family of choice +pub struct FamilyName { + /// Name of the font family + pub name: Atom, + /// Syntax of the font family + pub syntax: FamilyNameSyntax, +} + +impl ToCss for FamilyName { + fn to_css(&self, dest: &mut 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) + } + } + } +} + +#[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'. + 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), +} + +#[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), + /// Generic family name. + Generic(Atom), +} + +impl SingleFontFamily { + #[inline] + /// Get font family name as Atom + pub fn atom(&self) -> &Atom { + match *self { + SingleFontFamily::FamilyName(ref family_name) => &family_name.name, + SingleFontFamily::Generic(ref name) => name, + } + } + + #[inline] + #[cfg(not(feature = "gecko"))] // Gecko can't borrow atoms as UTF-8. + /// Get font family name + pub fn name(&self) -> &str { + self.atom() + } + + #[cfg(not(feature = "gecko"))] // Gecko can't borrow atoms as UTF-8. + /// Get the corresponding font-family with Atom + pub fn from_atom(input: Atom) -> SingleFontFamily { + match input { + atom!("serif") | + atom!("sans-serif") | + atom!("cursive") | + atom!("fantasy") | + atom!("monospace") => { + return SingleFontFamily::Generic(input) + } + _ => {} + } + match_ignore_ascii_case! { &input, + "serif" => return SingleFontFamily::Generic(atom!("serif")), + "sans-serif" => return SingleFontFamily::Generic(atom!("sans-serif")), + "cursive" => return SingleFontFamily::Generic(atom!("cursive")), + "fantasy" => return SingleFontFamily::Generic(atom!("fantasy")), + "monospace" => return SingleFontFamily::Generic(atom!("monospace")), + _ => {} + } + + // We don't know if it's quoted or not. So we set it to + // quoted by default. + SingleFontFamily::FamilyName(FamilyName { + name: input, + syntax: FamilyNameSyntax::Quoted, + }) + } + + /// Parse a font-family value + pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { + if let Ok(value) = input.try(|i| i.expect_string_cloned()) { + return Ok(SingleFontFamily::FamilyName(FamilyName { + name: Atom::from(&*value), + syntax: FamilyNameSyntax::Quoted, + })) + } + let first_ident = input.expect_ident()?.clone(); + + // 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(SingleFontFamily::Generic(atom!("serif"))), + "sans-serif" => return Ok(SingleFontFamily::Generic(atom!("sans-serif"))), + "cursive" => return Ok(SingleFontFamily::Generic(atom!("cursive"))), + "fantasy" => return Ok(SingleFontFamily::Generic(atom!("fantasy"))), + "monospace" => return Ok(SingleFontFamily::Generic(atom!("monospace"))), + + #[cfg(feature = "gecko")] + "-moz-fixed" => return Ok(SingleFontFamily::Generic(atom!("-moz-fixed"))), + + // 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.as_ref().to_owned(); + let mut serialization = String::new(); + serialize_identifier(&first_ident, &mut serialization).unwrap(); + + // 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(' '); + value.push_str(&ident); + serialization.push(' '); + serialize_identifier(&ident, &mut serialization).unwrap(); + } + while let Ok(ident) = input.try(|i| i.expect_ident_cloned()) { + 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), + })) + } + + #[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") { + (FontFamilyType::eFamily_serif, + structs::kGenericFont_serif) + } else if *name == atom!("sans-serif") { + (FontFamilyType::eFamily_sans_serif, + structs::kGenericFont_sans_serif) + } else if *name == atom!("cursive") { + (FontFamilyType::eFamily_cursive, + structs::kGenericFont_cursive) + } else if *name == atom!("fantasy") { + (FontFamilyType::eFamily_fantasy, + structs::kGenericFont_fantasy) + } else if *name == atom!("monospace") { + (FontFamilyType::eFamily_monospace, + structs::kGenericFont_monospace) + } else if *name == atom!("-moz-fixed") { + (FontFamilyType::eFamily_moz_fixed, + structs::kGenericFont_moz_fixed) + } else { + panic!("Unknown generic {}", name); + } + } + + #[cfg(feature = "gecko")] + /// Get the corresponding font-family with family name + fn from_font_family_name(family: &structs::FontFamilyName) -> SingleFontFamily { + use gecko_bindings::structs::FontFamilyType; + + match family.mType { + 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), + }) + }, + FontFamilyType::eFamily_named_quoted => SingleFontFamily::FamilyName(FamilyName { + name: (&*family.mName).into(), + syntax: FamilyNameSyntax::Quoted, + }), + x => panic!("Found unexpected font FontFamilyType: {:?}", x), + } + } +} + +impl ToCss for SingleFontFamily { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + SingleFontFamily::FamilyName(ref name) => name.to_css(dest), + + // All generic values accepted by the parser are known to not require escaping. + SingleFontFamily::Generic(ref name) => { + #[cfg(feature = "gecko")] { + // We should treat -moz-fixed as monospace + if name == &atom!("-moz-fixed") { + return dest.write_str("monospace"); + } + } + + write!(dest, "{}", name) + }, + } + } +} + +#[cfg(feature = "servo")] +#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)] +/// A list of SingleFontFamily +pub struct FontFamilyList(Box<[SingleFontFamily]>); + +#[cfg(feature = "gecko")] +#[derive(Clone, Debug)] +/// A list of SingleFontFamily +pub struct FontFamilyList(pub RefPtr); + +#[cfg(feature = "gecko")] +impl Hash for FontFamilyList { + fn hash(&self, state: &mut H) where H: Hasher { + for name in self.0.mNames.iter() { + name.mType.hash(state); + name.mName.hash(state); + } + } +} + +#[cfg(feature = "gecko")] +impl PartialEq for FontFamilyList { + fn eq(&self, other: &FontFamilyList) -> bool { + if self.0.mNames.len() != other.0.mNames.len() { + return false; + } + for (a, b) in self.0.mNames.iter().zip(other.0.mNames.iter()) { + if a.mType != b.mType || &*a.mName != &*b.mName { + return false; + } + } + true + } +} + +#[cfg(feature = "gecko")] +impl Eq for FontFamilyList {} + +impl FontFamilyList { + #[cfg(feature = "servo")] + /// Return FontFamilyList with a vector of SingleFontFamily + pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList { + FontFamilyList(families) + } + + #[cfg(feature = "gecko")] + /// Return FontFamilyList with a vector of SingleFontFamily + pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList { + let fontlist; + let names; + unsafe { + fontlist = bindings::Gecko_SharedFontList_Create(); + names = &mut (*fontlist).mNames; + names.ensure_capacity(families.len()); + }; + + for family in families.iter() { + match *family { + SingleFontFamily::FamilyName(ref f) => { + let quoted = matches!(f.syntax, FamilyNameSyntax::Quoted); + unsafe { + bindings::Gecko_nsTArray_FontFamilyName_AppendNamed( + names, + f.name.as_ptr(), + quoted + ); + } + } + SingleFontFamily::Generic(ref name) => { + let (family_type, _generic) = SingleFontFamily::generic(name); + unsafe { + bindings::Gecko_nsTArray_FontFamilyName_AppendGeneric( + names, + family_type + ); + } + } + } + } + + FontFamilyList(unsafe { RefPtr::from_addrefed(fontlist) }) + } + + #[cfg(feature = "servo")] + /// Return iterator of SingleFontFamily + pub fn iter(&self) -> slice::Iter { + self.0.iter() + } + + #[cfg(feature = "gecko")] + /// Return iterator of SingleFontFamily + pub fn iter(&self) -> FontFamilyNameIter { + FontFamilyNameIter { + names: &self.0.mNames, + cur: 0, + } + } + + #[cfg(feature = "gecko")] + /// Return the generic ID if it is a single generic font + pub fn single_generic(&self) -> Option { + let mut iter = self.iter(); + if let Some(SingleFontFamily::Generic(ref name)) = iter.next() { + if iter.next().is_none() { + return Some(SingleFontFamily::generic(name).1); + } + } + None + } +} + +#[cfg(feature = "gecko")] +/// Iterator of FontFamily +pub struct FontFamilyNameIter<'a> { + names: &'a structs::nsTArray, + cur: usize, +} + +#[cfg(feature = "gecko")] +impl<'a> Iterator for FontFamilyNameIter<'a> { + type Item = SingleFontFamily; + + fn next(&mut self) -> Option { + if self.cur < self.names.len() { + let item = SingleFontFamily::from_font_family_name(&self.names[self.cur]); + self.cur += 1; + Some(item) + } else { + None + } + } +} + #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss)] /// Preserve the readability of text when font fallback occurs pub enum FontSizeAdjust { diff --git a/servo/components/style/values/computed/mod.rs b/servo/components/style/values/computed/mod.rs index 342633ed8a62..9c8b1f18bc0e 100644 --- a/servo/components/style/values/computed/mod.rs +++ b/servo/components/style/values/computed/mod.rs @@ -37,7 +37,7 @@ pub use self::background::{BackgroundSize, BackgroundRepeat}; pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth}; pub use self::border::{BorderRadius, BorderCornerRadius, BorderSpacing}; pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, FontVariantAlternates}; -pub use self::font::{FontLanguageOverride, FontVariantSettings, FontVariantEastAsian}; +pub use self::font::{FontFamily, FontLanguageOverride, FontVariantSettings, FontVariantEastAsian}; pub use self::font::{FontVariantLigatures, FontVariantNumeric, FontFeatureSettings}; pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XTextZoom, XLang}; pub use self::box_::{AnimationIterationCount, AnimationName, OverscrollBehavior, ScrollSnapType, VerticalAlign}; diff --git a/servo/components/style/values/specified/font.rs b/servo/components/style/values/specified/font.rs index cd883ec243f1..fdf6eb171a0f 100644 --- a/servo/components/style/values/specified/font.rs +++ b/servo/components/style/values/specified/font.rs @@ -8,6 +8,10 @@ use Atom; use app_units::Au; use byteorder::{BigEndian, ByteOrder}; use cssparser::{Parser, Token}; +#[cfg(feature = "gecko")] +use gecko_bindings::bindings; +#[cfg(feature = "gecko")] +use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use parser::{Parse, ParserContext}; use properties::longhands::system_font::SystemFont; #[allow(unused_imports)] @@ -16,6 +20,7 @@ use std::fmt; use style_traits::{ToCss, StyleParseErrorKind, ParseError}; use values::CustomIdent; use values::computed::{font as computed, Context, Length, NonNegativeLength, ToComputedValue}; +use values::computed::font::{SingleFontFamily, FontFamilyList, FamilyName}; use values::generics::{FontSettings, FontSettingTagFloat}; use values::specified::{AllowQuirks, LengthOrPercentage, NoCalcLength, Number}; use values::specified::length::{AU_PER_PT, AU_PER_PX, FontBaseSize}; @@ -156,6 +161,128 @@ impl From for FontSize { } } +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +/// Specifies a prioritized list of font family names or generic family names +pub enum FontFamily { + /// List of `font-family` + Values(FontFamilyList), + /// System font + System(SystemFont), +} + +impl FontFamily { + /// Get `font-family` with system font + pub fn system_font(f: SystemFont) -> Self { + FontFamily::System(f) + } + + /// Get system font + pub fn get_system(&self) -> Option { + if let FontFamily::System(s) = *self { + Some(s) + } else { + None + } + } + + /// Parse a specified font-family value + pub fn parse_specified<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { + input.parse_comma_separated(|input| SingleFontFamily::parse(input)).map(|v| { + FontFamily::Values(FontFamilyList::new(v.into_boxed_slice())) + }) + } + + #[cfg(feature = "gecko")] + /// Return the generic ID if it is a single generic font + pub fn single_generic(&self) -> Option { + match *self { + FontFamily::Values(ref values) => values.single_generic(), + _ => None, + } + } +} + +impl ToComputedValue for FontFamily { + type ComputedValue = computed::FontFamily; + + fn to_computed_value(&self, _cx: &Context) -> Self::ComputedValue { + match *self { + FontFamily::Values(ref v) => computed::FontFamily(v.clone()), + FontFamily::System(_) => { + #[cfg(feature = "gecko")] { + _cx.cached_system_font.as_ref().unwrap().font_family.clone() + } + #[cfg(feature = "servo")] { + unreachable!() + } + } + } + } + + fn from_computed_value(other: &computed::FontFamily) -> Self { + FontFamily::Values(other.0.clone()) + } +} + +#[cfg(feature = "gecko")] +impl MallocSizeOf for FontFamily { + fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { + match *self { + FontFamily::Values(ref v) => { + // Although a SharedFontList object is refcounted, we always + // attribute its size to the specified value. + unsafe { + bindings::Gecko_SharedFontList_SizeOfIncludingThis( + v.0.get() + ) + } + } + FontFamily::System(_) => 0, + } + } +} + +impl ToCss for FontFamily { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + FontFamily::Values(ref v) => { + let mut iter = v.iter(); + iter.next().unwrap().to_css(dest)?; + for family in iter { + dest.write_str(", ")?; + family.to_css(dest)?; + } + Ok(()) + } + FontFamily::System(sys) => sys.to_css(dest), + } + } +} + +impl Parse for FontFamily { + /// # + /// = | [ + ] + /// TODO: + fn parse<'i, 't>( + _: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result> { + FontFamily::parse_specified(input) + } +} + +/// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other way around +/// because we want the former to exclude generic family keywords. +impl Parse for FamilyName { + fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { + match SingleFontFamily::parse(input) { + Ok(SingleFontFamily::FamilyName(name)) => Ok(name), + Ok(SingleFontFamily::Generic(_)) => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), + Err(e) => Err(e) + } + } +} + #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)] /// Preserve the readability of text when font fallback occurs pub enum FontSizeAdjust { diff --git a/servo/components/style/values/specified/mod.rs b/servo/components/style/values/specified/mod.rs index 72e9343641ea..1b6b19e8a7b0 100644 --- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -31,7 +31,7 @@ pub use self::background::{BackgroundRepeat, BackgroundSize}; pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth}; pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth, BorderSpacing}; pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, FontVariantAlternates}; -pub use self::font::{FontLanguageOverride, FontVariantSettings, FontVariantEastAsian}; +pub use self::font::{FontFamily, FontLanguageOverride, FontVariantSettings, FontVariantEastAsian}; pub use self::font::{FontVariantLigatures, FontVariantNumeric, FontFeatureSettings}; pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XTextZoom, XLang}; pub use self::box_::{AnimationIterationCount, AnimationName, OverscrollBehavior, ScrollSnapType, VerticalAlign}; diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs index 9905d3a9eab8..c0b60adaad50 100644 --- a/servo/ports/geckolib/glue.rs +++ b/servo/ports/geckolib/glue.rs @@ -3337,7 +3337,7 @@ pub extern "C" fn Servo_DeclarationBlock_SetFontFamily( let string = unsafe { (*value).to_string() }; let mut input = ParserInput::new(&string); let mut parser = Parser::new(&mut input); - let result = FontFamily::parse(&mut parser); + let result = FontFamily::parse_specified(&mut parser); if let Ok(family) = result { if parser.is_exhausted() { let decl = PropertyDeclaration::FontFamily(family);