mirror of
https://github.com/servo/servo.git
synced 2025-10-04 02:29:12 +01:00
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:
parent
837e41c8eb
commit
67f9b97735
8 changed files with 180 additions and 294 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue