From 4d9dd4b757ee73025d40b94144735ccbdbea73e5 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Fri, 29 Sep 2017 17:43:22 +0800 Subject: [PATCH] style: Use a SharedFontList object to store font-family values for Gecko. --- components/gfx/font_context.rs | 2 +- components/script/dom/element.rs | 5 +- .../style/gecko_bindings/sugar/refptr.rs | 3 + components/style/properties/gecko.mako.rs | 78 ++---- .../style/properties/longhand/font.mako.rs | 241 ++++++++++++++++-- ports/geckolib/glue.rs | 2 +- 6 files changed, 242 insertions(+), 89 deletions(-) diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index 7c0b36800c2..b5d2298d1f7 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -135,7 +135,7 @@ impl FontContext { let mut fonts: SmallVec<[Rc>; 8]> = SmallVec::new(); - for family in &style.font_family.0 { + for family in style.font_family.0.iter() { // GWTODO: Check on real pages if this is faster as Vec() or HashMap(). let mut cache_hit = false; for cached_font_entry in &self.layout_font_cache { diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 67e9057c397..3e8dd5f95b3 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -554,9 +554,10 @@ impl LayoutElementHelpers for LayoutDom { hints.push(from_declaration( shared_lock, PropertyDeclaration::FontFamily( - font_family::SpecifiedValue::Values(vec![ + font_family::SpecifiedValue::Values( + font_family::computed_value::FontFamilyList::new(vec![ font_family::computed_value::FontFamily::from_atom( - font_family)])))); + font_family)]))))); } let font_size = self.downcast::().and_then(|this| this.get_size()); diff --git a/components/style/gecko_bindings/sugar/refptr.rs b/components/style/gecko_bindings/sugar/refptr.rs index d27ef7205d4..adee2e8ea57 100644 --- a/components/style/gecko_bindings/sugar/refptr.rs +++ b/components/style/gecko_bindings/sugar/refptr.rs @@ -286,4 +286,7 @@ impl_threadsafe_refcount!(::gecko_bindings::structs::mozilla::css::GridTemplateA impl_threadsafe_refcount!(::gecko_bindings::structs::ImageValue, Gecko_AddRefImageValueArbitraryThread, Gecko_ReleaseImageValueArbitraryThread); +impl_threadsafe_refcount!(::gecko_bindings::structs::SharedFontList, + Gecko_AddRefSharedFontListArbitraryThread, + Gecko_ReleaseSharedFontListArbitraryThread); diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 9b55a1f6273..9501cf0e839 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -25,9 +25,6 @@ use gecko_bindings::bindings::Gecko_CopyFontFamilyFrom; use gecko_bindings::bindings::Gecko_CopyImageValueFrom; use gecko_bindings::bindings::Gecko_CopyListStyleImageFrom; use gecko_bindings::bindings::Gecko_EnsureImageLayersLength; -use gecko_bindings::bindings::Gecko_FontFamilyList_AppendGeneric; -use gecko_bindings::bindings::Gecko_FontFamilyList_AppendNamed; -use gecko_bindings::bindings::Gecko_FontFamilyList_Clear; use gecko_bindings::bindings::Gecko_SetCursorArrayLength; use gecko_bindings::bindings::Gecko_SetCursorImageValue; use gecko_bindings::bindings::Gecko_StyleTransition_SetUnsupportedProperty; @@ -2035,28 +2032,11 @@ fn static_assert() { } pub fn set_font_family(&mut self, v: longhands::font_family::computed_value::T) { - use properties::longhands::font_family::computed_value::{FontFamily, FamilyNameSyntax}; - - let list = &mut self.gecko.mFont.fontlist; - unsafe { Gecko_FontFamilyList_Clear(list); } - self.gecko.mGenericID = structs::kGenericFont_NONE; - - for family in &v.0 { - match *family { - FontFamily::FamilyName(ref f) => { - let quoted = matches!(f.syntax, FamilyNameSyntax::Quoted); - unsafe { Gecko_FontFamilyList_AppendNamed(list, f.name.as_ptr(), quoted); } - } - FontFamily::Generic(ref name) => { - let (family_type, generic) = FontFamily::generic(name); - if v.0.len() == 1 { - self.gecko.mGenericID = generic; - } - unsafe { Gecko_FontFamilyList_AppendGeneric(list, family_type); } - } - } + if let Some(generic) = v.0.single_generic() { + self.gecko.mGenericID = generic; } + self.gecko.mFont.fontlist.mFontlist.mBasePtr.set_move((v.0).0.clone()); } pub fn font_family_count(&self) -> usize { @@ -2078,46 +2058,28 @@ fn static_assert() { } pub fn clone_font_family(&self) -> longhands::font_family::computed_value::T { - use cssparser::serialize_identifier; - use properties::longhands::font_family::computed_value::{FontFamily, FamilyName, FamilyNameSyntax}; use gecko_bindings::structs::FontFamilyType; - use gecko_string_cache::Atom; + use properties::longhands::font_family::computed_value; + use properties::longhands::font_family::computed_value::FontFamily; + use properties::longhands::font_family::computed_value::FontFamilyList; - if self.gecko.mFont.fontlist.mFontlist.is_empty() { - let default = match self.gecko.mFont.fontlist.mDefaultFontType { - FontFamilyType::eFamily_serif => FontFamily::Generic(atom!("serif")), - FontFamilyType::eFamily_sans_serif => FontFamily::Generic(atom!("sans-serif")), + let fontlist = &self.gecko.mFont.fontlist; + let shared_fontlist = unsafe { fontlist.mFontlist.mBasePtr.to_safe() }; + + if shared_fontlist.mNames.is_empty() { + let default = match fontlist.mDefaultFontType { + FontFamilyType::eFamily_serif => { + FontFamily::Generic(atom!("serif")) + } + FontFamilyType::eFamily_sans_serif => { + FontFamily::Generic(atom!("sans-serif")) + } _ => panic!("Default generic must be serif or sans-serif"), }; - return longhands::font_family::computed_value::T(vec![default]); + computed_value::T(FontFamilyList::new(vec![default])) + } else { + computed_value::T(FontFamilyList(shared_fontlist)) } - - longhands::font_family::computed_value::T( - self.gecko.mFont.fontlist.mFontlist.iter().map(|gecko_font_family_name| { - match gecko_font_family_name.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(&*gecko_font_family_name.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: (&*gecko_font_family_name.mName).into(), - syntax: FamilyNameSyntax::Quoted, - }), - x => panic!("Found unexpected font FontFamilyType: {:?}", x), - } - }).collect() - ) } pub fn unzoom_fonts(&mut self, device: &Device) { diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 9cbfd2d088e..ac35c271ec2 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -68,20 +68,27 @@ macro_rules! impl_gecko_keyword_conversions { } -<%helpers:longhand name="font-family" animation_value_type="discrete" boxed="${product == 'gecko'}" +<%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, FamilyName}; + 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}; - use Atom; + #[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, PartialEq)] @@ -223,8 +230,8 @@ macro_rules! impl_gecko_keyword_conversions { #[cfg(feature = "gecko")] /// Return the generic ID for a given generic font name - pub fn generic(name: &Atom) -> (::gecko_bindings::structs::FontFamilyType, u8) { - use gecko_bindings::structs::{self, FontFamilyType}; + pub fn generic(name: &Atom) -> (structs::FontFamilyType, u8) { + use gecko_bindings::structs::FontFamilyType; if *name == atom!("serif") { (FontFamilyType::eFamily_serif, structs::kGenericFont_serif) @@ -247,6 +254,34 @@ macro_rules! impl_gecko_keyword_conversions { 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 { @@ -298,15 +333,156 @@ macro_rules! impl_gecko_keyword_conversions { } } + #[cfg(feature = "servo")] + #[derive(Clone, Debug, Eq, Hash, HeapSizeOf, 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 = "gecko", derive(MallocSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub struct T(pub Vec); + 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(vec![FontFamily::Generic(atom!("serif"))]) + computed_value::T( + FontFamilyList::new(vec![FontFamily::Generic(atom!("serif"))]) + ) } /// # @@ -317,10 +493,9 @@ macro_rules! impl_gecko_keyword_conversions { SpecifiedValue::parse(input) } - #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum SpecifiedValue { - Values(Vec), + Values(FontFamilyList), System(SystemFont), } @@ -328,14 +503,10 @@ macro_rules! impl_gecko_keyword_conversions { impl SpecifiedValue { /// Return the generic ID if it is a single generic font pub fn single_generic(&self) -> Option { - if let SpecifiedValue::Values(ref values) = *self { - if values.len() == 1 { - if let FontFamily::Generic(ref name) = values[0] { - return Some(FontFamily::generic(name).1); - } - } + match *self { + SpecifiedValue::Values(ref values) => values.single_generic(), + _ => None, } - None } } @@ -356,6 +527,24 @@ macro_rules! impl_gecko_keyword_conversions { } } + #[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) @@ -369,11 +558,12 @@ macro_rules! impl_gecko_keyword_conversions { } pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { - input.parse_comma_separated(|input| FontFamily::parse(input)).map(SpecifiedValue::Values) + 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 { @@ -2191,16 +2381,13 @@ ${helpers.single_keyword("-moz-math-variant", cx.device().pres_context() ) } - let family = system.fontlist.mFontlist.iter().map(|font| { - use properties::longhands::font_family::computed_value::*; - FontFamily::FamilyName(FamilyName { - name: (&*font.mName).into(), - syntax: FamilyNameSyntax::Quoted, - }) - }).collect::>(); let weight = longhands::font_weight::computed_value::T::from_gecko_weight(system.weight); let ret = ComputedSystemFont { - font_family: longhands::font_family::computed_value::T(family), + font_family: longhands::font_family::computed_value::T( + longhands::font_family::computed_value::FontFamilyList( + unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() } + ) + ), font_size: longhands::font_size::computed_value::T { size: Au(system.size).into(), keyword_info: None diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 864158e0b2f..fc88368603c 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -2970,7 +2970,7 @@ pub extern "C" fn Servo_DeclarationBlock_SetFontFamily(declarations: let result = FontFamily::parse(&mut parser); if let Ok(family) = result { if parser.is_exhausted() { - let decl = PropertyDeclaration::FontFamily(Box::new(family)); + let decl = PropertyDeclaration::FontFamily(family); write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| { decls.push(decl, Importance::Normal); })