style: Unify font-family storage

This changes font-family storage to reuse the rust types, removing a
bunch of code while at it. This allows us to, for example, use a single
static font family for -moz-bullet and clone it, rather than creating a
lot of expensive copies.

Differential Revision: https://phabricator.services.mozilla.com/D118011
This commit is contained in:
Emilio Cobos Álvarez 2023-05-22 01:07:30 +02:00 committed by Oriol Brufau
parent 837e41c8eb
commit 67f9b97735
8 changed files with 180 additions and 294 deletions

View file

@ -4,8 +4,6 @@
//! Computed values for font properties
#[cfg(feature = "gecko")]
use crate::gecko_bindings::sugar::refptr::RefPtr;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::{bindings, structs};
use crate::values::animated::ToAnimatedValue;
@ -26,13 +24,7 @@ use cssparser::{serialize_identifier, CssStringWriter, Parser};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use std::fmt::{self, Write};
use std::hash::{Hash, Hasher};
#[cfg(feature = "gecko")]
use std::mem::{self, ManuallyDrop};
#[cfg(feature = "servo")]
use std::slice;
use style_traits::{CssWriter, ParseError, ToCss};
#[cfg(feature = "gecko")]
use to_shmem::{self, SharedMemoryBuilder, ToShmem};
pub use crate::values::computed::Length as MozScriptMinSize;
pub use crate::values::specified::font::{FontSynthesis, MozScriptSizeMultiplier};
@ -183,6 +175,7 @@ impl ToAnimatedValue for FontSize {
#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)]
#[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf, Serialize, Deserialize))]
/// Specifies a prioritized list of font family names or generic family names.
#[repr(C)]
pub struct FontFamily {
/// The actual list of family names.
pub families: FontFamilyList,
@ -190,27 +183,95 @@ pub struct FontFamily {
pub is_system_font: bool,
}
macro_rules! static_font_family {
($ident:ident, $family:expr) => {
lazy_static! {
static ref $ident: FontFamily = FontFamily {
families: FontFamilyList {
list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
fallback: GenericFontFamily::None,
},
is_system_font: false,
};
}
};
}
impl FontFamily {
#[inline]
/// Get default font family as `serif` which is a generic font-family
pub fn serif() -> Self {
FontFamily {
families: FontFamilyList::new(Box::new([SingleFontFamily::Generic(
GenericFontFamily::Serif,
)])),
is_system_font: false,
Self::generic(GenericFontFamily::Serif).clone()
}
/// Returns the font family for `-moz-bullet-font`.
pub(crate) fn moz_bullet() -> &'static Self {
static_font_family!(MOZ_BULLET, SingleFontFamily::FamilyName(FamilyName {
name: atom!("-moz-bullet-font"),
syntax: FontFamilyNameSyntax::Identifiers,
}));
&*MOZ_BULLET
}
/// Returns a font family for a single system font.
pub fn for_system_font(name: &str) -> Self {
Self {
families: FontFamilyList {
list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(FamilyName {
name: Atom::from(name),
syntax: FontFamilyNameSyntax::Identifiers,
}))),
fallback: GenericFontFamily::None,
},
is_system_font: true,
}
}
/// Returns a generic font family.
pub fn generic(generic: GenericFontFamily) -> &'static Self {
macro_rules! generic_font_family {
($ident:ident, $family:ident) => {
static_font_family!($ident, SingleFontFamily::Generic(GenericFontFamily::$family))
}
}
generic_font_family!(SERIF, Serif);
generic_font_family!(SANS_SERIF, SansSerif);
generic_font_family!(MONOSPACE, Monospace);
generic_font_family!(CURSIVE, Cursive);
generic_font_family!(FANTASY, Fantasy);
generic_font_family!(MOZ_EMOJI, MozEmoji);
match generic {
GenericFontFamily::None => {
debug_assert!(false, "Bogus caller!");
&*SERIF
}
GenericFontFamily::Serif => &*SERIF,
GenericFontFamily::SansSerif => &*SANS_SERIF,
GenericFontFamily::Monospace => &*MONOSPACE,
GenericFontFamily::Cursive => &*CURSIVE,
GenericFontFamily::Fantasy => &*FANTASY,
GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
}
}
}
#[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.
let shared_font_list = self.families.shared_font_list().get();
unsafe { bindings::Gecko_SharedFontList_SizeOfIncludingThisIfUnshared(shared_font_list) }
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
use malloc_size_of::MallocUnconditionalSizeOf;
// SharedFontList objects are generally measured from the pointer stored
// in the specified value. So only count this if the SharedFontList is
// unshared.
let shared_font_list = &self.families.list;
if shared_font_list.is_unique() {
shared_font_list.unconditional_size_of(ops)
} else {
0
}
}
}
@ -220,7 +281,10 @@ impl ToCss for FontFamily {
W: fmt::Write,
{
let mut iter = self.families.iter();
iter.next().unwrap().to_css(dest)?;
match iter.next() {
Some(f) => f.to_css(dest)?,
None => return self.families.fallback.to_css(dest),
}
for family in iter {
dest.write_str(", ")?;
family.to_css(dest)?;
@ -229,15 +293,16 @@ impl ToCss for FontFamily {
}
}
/// The name of a font family of choice.
#[derive(
Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
/// The name of a font family of choice
#[repr(C)]
pub struct FamilyName {
/// Name of the font family
/// Name of the font family.
pub name: Atom,
/// Syntax of the font family
/// Syntax of the font family.
pub syntax: FontFamilyNameSyntax,
}
@ -291,11 +356,13 @@ pub enum FontFamilyNameSyntax {
Identifiers,
}
/// A set of faces that vary in weight, width or slope.
/// cbindgen:derive-mut-casts=true
#[derive(
Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
/// A set of faces that vary in weight, width or slope.
#[repr(u8)]
pub enum SingleFontFamily {
/// The name of a font family of choice.
FamilyName(FamilyName),
@ -429,145 +496,57 @@ impl SingleFontFamily {
syntax: FontFamilyNameSyntax::Quoted,
})
}
#[cfg(feature = "gecko")]
/// Get the corresponding font-family with family name
fn from_font_family_name(family: &structs::FontFamilyName) -> SingleFontFamily {
if family.mName.mRawPtr.is_null() {
debug_assert_ne!(family.mGeneric, GenericFontFamily::None);
return SingleFontFamily::Generic(family.mGeneric);
}
let name = unsafe { Atom::from_raw(family.mName.mRawPtr) };
SingleFontFamily::FamilyName(FamilyName {
name,
syntax: family.mSyntax,
})
}
}
#[cfg(feature = "servo")]
#[derive(
Clone,
Debug,
Deserialize,
Eq,
Hash,
MallocSizeOf,
PartialEq,
Serialize,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
/// A list of SingleFontFamily
pub struct FontFamilyList(Box<[SingleFontFamily]>);
#[cfg(feature = "gecko")]
#[derive(Clone, Debug, ToComputedValue, ToResolvedValue)]
/// A list of SingleFontFamily
pub enum FontFamilyList {
/// A strong reference to a Gecko SharedFontList object.
SharedFontList(
#[compute(no_field_bound)]
#[resolve(no_field_bound)]
RefPtr<structs::SharedFontList>,
),
/// A font-family generic ID.
Generic(GenericFontFamily),
/// A list of font families.
#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
#[repr(C)]
pub struct FontFamilyList {
/// The actual list of font families specified.
pub list: crate::ArcSlice<SingleFontFamily>,
/// A fallback font type (none, serif, or sans-serif, generally).
pub fallback: GenericFontFamily,
}
#[cfg(feature = "gecko")]
impl ToShmem for FontFamilyList {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
// In practice, the only SharedFontList objects we create from shared
// style sheets are ones with a single generic entry.
Ok(ManuallyDrop::new(match *self {
FontFamilyList::SharedFontList(ref r) => {
if !(r.mNames.len() == 1 && r.mNames[0].mName.mRawPtr.is_null()) {
return Err(String::from(
"ToShmem failed for FontFamilyList: cannot handle non-generic families",
));
}
FontFamilyList::Generic(r.mNames[0].mGeneric)
},
FontFamilyList::Generic(t) => FontFamilyList::Generic(t),
}))
}
}
#[cfg(feature = "gecko")]
impl PartialEq for FontFamilyList {
fn eq(&self, other: &FontFamilyList) -> bool {
let self_list = self.shared_font_list();
let other_list = other.shared_font_list();
if self_list.mNames.len() != other_list.mNames.len() {
return false;
}
for (a, b) in self_list.mNames.iter().zip(other_list.mNames.iter()) {
if a.mSyntax != b.mSyntax ||
a.mName.mRawPtr != b.mName.mRawPtr ||
a.mGeneric != b.mGeneric
{
return false;
}
}
true
}
}
#[cfg(feature = "gecko")]
impl Eq for FontFamilyList {}
impl FontFamilyList {
/// Return FontFamilyList with a vector of SingleFontFamily
#[cfg(feature = "servo")]
pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList {
FontFamilyList(families)
/// Return iterator of SingleFontFamily
pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
self.list.iter()
}
/// Return FontFamilyList with a vector of SingleFontFamily
#[cfg(feature = "gecko")]
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());
/// Puts the fallback in the list if needed.
pub fn normalize(&mut self) {
if self.fallback == GenericFontFamily::None {
return;
}
let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
new_list.push(SingleFontFamily::Generic(self.fallback));
self.list = crate::ArcSlice::from_iter(new_list.into_iter());
}
/// If there's a generic font family on the list (which isn't cursive or
/// fantasy), then move it to the front of the list. Otherwise, prepend the
/// default generic.
pub (crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
let index_of_first_generic = self.iter().position(|f| {
match *f {
SingleFontFamily::Generic(f) => f != GenericFontFamily::Cursive && f != GenericFontFamily::Fantasy,
_ => false,
}
});
if let Some(0) = index_of_first_generic {
return; // Already first
}
let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
let element_to_prepend = match index_of_first_generic {
Some(i) => new_list.remove(i),
None => SingleFontFamily::Generic(generic),
};
for family in families.iter() {
match *family {
SingleFontFamily::FamilyName(ref f) => unsafe {
bindings::Gecko_nsTArray_FontFamilyName_AppendNamed(
names,
f.name.as_ptr(),
f.syntax,
);
},
SingleFontFamily::Generic(family) => unsafe {
bindings::Gecko_nsTArray_FontFamilyName_AppendGeneric(names, family);
},
}
}
FontFamilyList::SharedFontList(unsafe { RefPtr::from_addrefed(fontlist) })
}
/// Return iterator of SingleFontFamily
#[cfg(feature = "servo")]
pub fn iter(&self) -> slice::Iter<SingleFontFamily> {
self.0.iter()
}
/// Return iterator of SingleFontFamily
#[cfg(feature = "gecko")]
pub fn iter(&self) -> FontFamilyNameIter {
FontFamilyNameIter {
names: &self.shared_font_list().mNames,
cur: 0,
}
new_list.insert(0, element_to_prepend);
self.list = crate::ArcSlice::from_iter(new_list.into_iter());
}
/// Return the generic ID if it is a single generic font
@ -580,46 +559,6 @@ impl FontFamilyList {
}
None
}
/// Return a reference to the Gecko SharedFontList.
#[cfg(feature = "gecko")]
pub fn shared_font_list(&self) -> &RefPtr<structs::SharedFontList> {
match *self {
FontFamilyList::SharedFontList(ref r) => r,
FontFamilyList::Generic(t) => {
unsafe {
// TODO(heycam): Should really add StaticRefPtr sugar.
let index = t as usize;
mem::transmute::<
&structs::StaticRefPtr<structs::SharedFontList>,
&RefPtr<structs::SharedFontList>,
>(&structs::SharedFontList_sSingleGenerics[index])
}
},
}
}
}
/// Iterator of FontFamily
#[cfg(feature = "gecko")]
pub struct FontFamilyNameIter<'a> {
names: &'a structs::nsTArray<structs::FontFamilyName>,
cur: usize,
}
#[cfg(feature = "gecko")]
impl<'a> Iterator for FontFamilyNameIter<'a> {
type Item = SingleFontFamily;
fn next(&mut self) -> Option<Self::Item> {
if self.cur < self.names.len() {
let item = SingleFontFamily::from_font_family_name(&self.names[self.cur]);
self.cur += 1;
Some(item)
} else {
None
}
}
}
/// Preserve the readability of text when font fallback occurs

View file

@ -6,8 +6,6 @@
#[cfg(feature = "gecko")]
use crate::context::QuirksMode;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::bindings;
use crate::parser::{Parse, ParserContext};
use crate::values::computed::font::{FamilyName, FontFamilyList, FontStyleAngle, SingleFontFamily};
use crate::values::computed::{font as computed, Length, NonNegativeLength};
@ -24,7 +22,7 @@ use crate::values::CustomIdent;
use crate::Atom;
use cssparser::{Parser, Token};
#[cfg(feature = "gecko")]
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
use std::fmt::{self, Write};
use style_traits::values::SequenceWriter;
use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
@ -687,9 +685,10 @@ impl FontFamily {
/// Parse a specified font-family value
pub fn parse_specified<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
let values = input.parse_comma_separated(SingleFontFamily::parse)?;
Ok(FontFamily::Values(FontFamilyList::new(
values.into_boxed_slice(),
)))
Ok(FontFamily::Values(FontFamilyList {
list: crate::ArcSlice::from_iter(values.into_iter()),
fallback: computed::GenericFontFamily::None,
}))
}
}
@ -698,8 +697,8 @@ impl ToComputedValue for FontFamily {
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self {
FontFamily::Values(ref v) => computed::FontFamily {
families: v.clone(),
FontFamily::Values(ref list) => computed::FontFamily {
families: list.clone(),
is_system_font: false,
},
FontFamily::System(_) => self.compute_system(context),
@ -713,18 +712,12 @@ impl ToComputedValue for FontFamily {
#[cfg(feature = "gecko")]
impl MallocSizeOf for FontFamily {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
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, as long as it's
// not a value in SharedFontList::sSingleGenerics.
if matches!(v, FontFamilyList::SharedFontList(_)) {
let ptr = v.shared_font_list().get();
unsafe { bindings::Gecko_SharedFontList_SizeOfIncludingThis(ptr) }
} else {
0
}
// Although the family list is refcounted, we always attribute
// its size to the specified value.
v.list.unconditional_size_of(ops)
},
FontFamily::System(_) => 0,
}