aboutsummaryrefslogtreecommitdiffstats
path: root/components/fonts
diff options
context:
space:
mode:
Diffstat (limited to 'components/fonts')
-rw-r--r--components/fonts/font.rs49
-rw-r--r--components/fonts/font_context.rs288
-rw-r--r--components/fonts/font_store.rs112
-rw-r--r--components/fonts/font_template.rs67
-rw-r--r--components/fonts/lib.rs4
-rw-r--r--components/fonts/platform/freetype/android/font_list.rs2
-rw-r--r--components/fonts/platform/freetype/font.rs2
-rw-r--r--components/fonts/platform/freetype/ohos/font_list.rs2
-rw-r--r--components/fonts/platform/macos/core_text_font_cache.rs2
-rw-r--r--components/fonts/system_font_service.rs (renamed from components/fonts/font_cache_thread.rs)302
-rw-r--r--components/fonts/tests/font_context.rs146
11 files changed, 519 insertions, 457 deletions
diff --git a/components/fonts/font.rs b/components/fonts/font.rs
index e423c546dae..3af077b4e06 100644
--- a/components/fonts/font.rs
+++ b/components/fonts/font.rs
@@ -27,14 +27,14 @@ use style::values::computed::{FontStretch, FontStyle, FontWeight};
use unicode_script::Script;
use webrender_api::{FontInstanceFlags, FontInstanceKey};
-use crate::font_cache_thread::{FontIdentifier, FontSource};
use crate::font_context::FontContext;
use crate::font_template::{FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods};
use crate::platform::font::{FontTable, PlatformFont};
pub use crate::platform::font_list::fallback_font_families;
+use crate::system_font_service::{FontIdentifier, SystemFontServiceProxyTrait};
use crate::{
- ByteIndex, EmojiPresentationPreference, FallbackFontSelectionOptions, GlyphData, GlyphId,
- GlyphStore, Shaper,
+ ByteIndex, EmojiPresentationPreference, FallbackFontSelectionOptions, FontData, GlyphData,
+ GlyphId, GlyphStore, Shaper,
};
#[macro_export]
@@ -66,11 +66,13 @@ pub trait PlatformFontMethods: Sized {
fn new_from_template(
template: FontTemplateRef,
pt_size: Option<Au>,
+ data: &Arc<Vec<u8>>,
) -> Result<PlatformFont, &'static str> {
- let data = template.data();
+ let template = template.borrow();
+
let face_index = template.identifier().index();
- let font_identifier = template.borrow().identifier.clone();
- Self::new_from_data(font_identifier, data, face_index, pt_size)
+ let font_identifier = template.identifier.clone();
+ Self::new_from_data(font_identifier, data.clone(), face_index, pt_size)
}
fn new_from_data(
@@ -218,6 +220,7 @@ impl malloc_size_of::MallocSizeOf for CachedShapeData {
#[derive(Debug)]
pub struct Font {
pub handle: PlatformFont,
+ pub data: Arc<FontData>,
pub template: FontTemplateRef,
pub metrics: FontMetrics,
pub descriptor: FontDescriptor,
@@ -251,13 +254,19 @@ impl Font {
pub fn new(
template: FontTemplateRef,
descriptor: FontDescriptor,
+ data: Arc<FontData>,
synthesized_small_caps: Option<FontRef>,
) -> Result<Font, &'static str> {
- let handle = PlatformFont::new_from_template(template.clone(), Some(descriptor.pt_size))?;
+ let handle = PlatformFont::new_from_template(
+ template.clone(),
+ Some(descriptor.pt_size),
+ data.as_arc(),
+ )?;
let metrics = handle.metrics();
Ok(Font {
handle,
+ data,
template,
shaper: OnceLock::new(),
descriptor,
@@ -526,7 +535,7 @@ impl FontGroup {
/// `codepoint`. If no such font is found, returns the first available font or fallback font
/// (which will cause a "glyph not found" character to be rendered). If no font at all can be
/// found, returns None.
- pub fn find_by_codepoint<S: FontSource>(
+ pub fn find_by_codepoint<S: SystemFontServiceProxyTrait>(
&mut self,
font_context: &FontContext<S>,
codepoint: char,
@@ -598,7 +607,10 @@ impl FontGroup {
}
/// Find the first available font in the group, or the first available fallback font.
- pub fn first<S: FontSource>(&mut self, font_context: &FontContext<S>) -> Option<FontRef> {
+ pub fn first<S: SystemFontServiceProxyTrait>(
+ &mut self,
+ font_context: &FontContext<S>,
+ ) -> Option<FontRef> {
// From https://drafts.csswg.org/css-fonts/#first-available-font:
// > The first available font, used for example in the definition of font-relative lengths
// > such as ex or in the definition of the line-height property, is defined to be the first
@@ -629,7 +641,7 @@ impl FontGroup {
font_predicate: FontPredicate,
) -> Option<FontRef>
where
- S: FontSource,
+ S: SystemFontServiceProxyTrait,
TemplatePredicate: Fn(FontTemplateRef) -> bool,
FontPredicate: Fn(&FontRef) -> bool,
{
@@ -659,7 +671,7 @@ impl FontGroup {
font_predicate: FontPredicate,
) -> Option<FontRef>
where
- S: FontSource,
+ S: SystemFontServiceProxyTrait,
TemplatePredicate: Fn(FontTemplateRef) -> bool,
FontPredicate: Fn(&FontRef) -> bool,
{
@@ -731,7 +743,7 @@ impl FontGroupFamily {
font_predicate: &FontPredicate,
) -> Option<FontRef>
where
- S: FontSource,
+ S: SystemFontServiceProxyTrait,
TemplatePredicate: Fn(FontTemplateRef) -> bool,
FontPredicate: Fn(&FontRef) -> bool,
{
@@ -754,7 +766,7 @@ impl FontGroupFamily {
.next()
}
- fn members<S: FontSource>(
+ fn members<S: SystemFontServiceProxyTrait>(
&mut self,
font_descriptor: &FontDescriptor,
font_context: &FontContext<S>,
@@ -883,6 +895,8 @@ pub(crate) fn map_platform_values_to_style_values(mapping: &[(f64, f64)], value:
#[cfg(test)]
mod test {
+ use crate::FontData;
+
#[cfg(target_os = "windows")]
#[test]
fn test_shape_text_fast() {
@@ -917,14 +931,16 @@ mod test {
let identifier = FontIdentifier::Web(ServoUrl::from_file_path(path.clone()).unwrap());
let file = File::open(path).unwrap();
- let data: Arc<Vec<u8>> = Arc::new(file.bytes().map(|b| b.unwrap()).collect());
+ let data = Arc::new(FontData::from_bytes(
+ file.bytes().map(|b| b.unwrap()).collect(),
+ ));
let platform_font =
- PlatformFont::new_from_data(identifier.clone(), data.clone(), 0, None).unwrap();
+ PlatformFont::new_from_data(identifier.clone(), data.as_arc().clone(), 0, None)
+ .unwrap();
let template = FontTemplate {
identifier,
descriptor: platform_font.descriptor(),
- data: Some(data),
stylesheet: None,
};
let descriptor = FontDescriptor {
@@ -937,6 +953,7 @@ mod test {
let font = Font::new(
Arc::new(atomic_refcell::AtomicRefCell::new(template)),
descriptor,
+ data,
None,
)
.unwrap();
diff --git a/components/fonts/font_context.rs b/components/fonts/font_context.rs
index 140fa56f14d..cdd97466341 100644
--- a/components/fonts/font_context.rs
+++ b/components/fonts/font_context.rs
@@ -19,6 +19,7 @@ use net_traits::request::{Destination, Referrer, RequestBuilder};
use net_traits::{fetch_async, CoreResourceThread, FetchResponseMsg, ResourceThreads};
use parking_lot::{Mutex, ReentrantMutex, RwLock};
use servo_arc::Arc as ServoArc;
+use servo_url::ServoUrl;
use style::computed_values::font_variant_caps::T as FontVariantCaps;
use style::font_face::{FontFaceSourceFormat, FontFaceSourceFormatKeyword, Source, UrlSource};
use style::media_queries::Device;
@@ -33,39 +34,67 @@ use webrender_api::{FontInstanceKey, FontKey};
use crate::font::{
Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope,
};
-use crate::font_cache_thread::{CSSFontFaceDescriptors, FontIdentifier, FontSource};
use crate::font_store::{CrossThreadFontStore, CrossThreadWebRenderFontStore};
use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods};
-use crate::LowercaseFontFamilyName;
+use crate::platform::font::PlatformFont;
+use crate::system_font_service::{
+ CSSFontFaceDescriptors, FontIdentifier, SystemFontServiceProxyTrait,
+};
+use crate::{FontData, LowercaseFontFamilyName, PlatformFontMethods};
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
/// The FontContext represents the per-thread/thread state necessary for
/// working with fonts. It is the public API used by the layout and
-/// paint code. It talks directly to the font cache thread where
+/// paint code. It talks directly to the system font service where
/// required.
-pub struct FontContext<S: FontSource> {
- font_source: ReentrantMutex<S>,
+pub struct FontContext<Proxy: SystemFontServiceProxyTrait> {
+ pub(crate) system_font_service_proxy: Arc<Proxy>,
resource_threads: ReentrantMutex<CoreResourceThread>,
- cache: CachingFontSource<S>,
+
+ /// The actual instances of fonts ie a [`FontTemplate`] combined with a size and
+ /// other font properties, along with the font data and a platform font instance.
+ fonts: RwLock<HashMap<FontCacheKey, Option<FontRef>>>,
+
+ /// A caching map between the specification of a font in CSS style and
+ /// resolved [`FontGroup`] which contains information about all fonts that
+ /// can be selected with that style.
+ resolved_font_groups:
+ RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>,
+
web_fonts: CrossThreadFontStore,
webrender_font_store: CrossThreadWebRenderFontStore,
have_removed_web_fonts: AtomicBool,
}
-impl<S: FontSource> MallocSizeOf for FontContext<S> {
+impl<S: SystemFontServiceProxyTrait> MallocSizeOf for FontContext<S> {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.cache.size_of(ops)
+ let font_cache_size = self
+ .fonts
+ .read()
+ .iter()
+ .map(|(key, font)| {
+ key.size_of(ops) + font.as_ref().map_or(0, |font| (*font).size_of(ops))
+ })
+ .sum::<usize>();
+ let font_group_cache_size = self
+ .resolved_font_groups
+ .read()
+ .iter()
+ .map(|(key, font_group)| key.size_of(ops) + (*font_group.read()).size_of(ops))
+ .sum::<usize>();
+ font_cache_size + font_group_cache_size
}
}
-impl<S: FontSource> FontContext<S> {
- pub fn new(font_source: S, resource_threads: ResourceThreads) -> FontContext<S> {
+impl<Proxy: SystemFontServiceProxyTrait> FontContext<Proxy> {
+ pub fn new(system_font_service_proxy: Arc<Proxy>, resource_threads: ResourceThreads) -> Self {
#[allow(clippy::default_constructed_unit_structs)]
- FontContext {
- font_source: ReentrantMutex::new(font_source.clone()),
+ Self {
+ system_font_service_proxy,
resource_threads: ReentrantMutex::new(resource_threads.core_thread),
- cache: CachingFontSource::new(font_source),
+ fonts: Default::default(),
+ resolved_font_groups: Default::default(),
web_fonts: Arc::new(RwLock::default()),
webrender_font_store: Arc::new(RwLock::default()),
have_removed_web_fonts: AtomicBool::new(false),
@@ -76,6 +105,14 @@ impl<S: FontSource> FontContext<S> {
self.web_fonts.read().number_of_fonts_still_loading()
}
+ pub(crate) fn get_font_data(&self, identifier: &FontIdentifier) -> Arc<FontData> {
+ self.web_fonts
+ .read()
+ .get_font_data(identifier)
+ .or_else(|| self.system_font_service_proxy.get_font_data(identifier))
+ .expect("Could not find font data")
+ }
+
/// Handle the situation where a web font finishes loading, specifying if the load suceeded or failed.
fn handle_web_font_load_finished(
&self,
@@ -83,7 +120,7 @@ impl<S: FontSource> FontContext<S> {
succeeded: bool,
) {
if succeeded {
- self.cache.invalidate_after_web_font_load();
+ self.invalidate_font_groups_after_web_font_load();
}
finished_callback(succeeded);
}
@@ -103,7 +140,19 @@ impl<S: FontSource> FontContext<S> {
style: ServoArc<FontStyleStruct>,
size: Au,
) -> Arc<RwLock<FontGroup>> {
- self.cache.font_group_with_size(style, size)
+ let cache_key = FontGroupCacheKey { size, style };
+ if let Some(font_group) = self.resolved_font_groups.read().get(&cache_key) {
+ return font_group.clone();
+ }
+
+ let mut descriptor = FontDescriptor::from(&*cache_key.style);
+ descriptor.pt_size = size;
+
+ let font_group = Arc::new(RwLock::new(FontGroup::new(&cache_key.style, descriptor)));
+ self.resolved_font_groups
+ .write()
+ .insert(cache_key, font_group.clone());
+ font_group
}
/// Returns a font matching the parameters. Fonts are cached, so repeated calls will return a
@@ -148,7 +197,7 @@ impl<S: FontSource> FontContext<S> {
font_descriptor: font_descriptor.clone(),
};
- if let Some(font) = self.cache.fonts.read().get(&cache_key).cloned() {
+ if let Some(font) = self.fonts.read().get(&cache_key).cloned() {
return font;
}
@@ -166,7 +215,7 @@ impl<S: FontSource> FontContext<S> {
synthesized_small_caps_font,
)
.ok();
- self.cache.fonts.write().insert(cache_key, font.clone());
+ self.fonts.write().insert(cache_key, font.clone());
font
}
@@ -192,7 +241,7 @@ impl<S: FontSource> FontContext<S> {
}
/// Try to find matching templates in this [`FontContext`], first looking in the list of web fonts and
- /// falling back to asking the [`super::FontCacheThread`] for a matching system font.
+ /// falling back to asking the [`super::SystemFontService`] for a matching system font.
pub fn matching_templates(
&self,
descriptor_to_match: &FontDescriptor,
@@ -200,8 +249,10 @@ impl<S: FontSource> FontContext<S> {
) -> Vec<FontTemplateRef> {
self.matching_web_font_templates(descriptor_to_match, family_descriptor)
.unwrap_or_else(|| {
- self.cache
- .matching_templates(descriptor_to_match, family_descriptor)
+ self.system_font_service_proxy.find_matching_font_templates(
+ Some(descriptor_to_match),
+ &family_descriptor.family,
+ )
})
}
@@ -216,18 +267,18 @@ impl<S: FontSource> FontContext<S> {
let mut font = Font::new(
font_template.clone(),
font_descriptor.clone(),
+ self.get_font_data(&font_template.identifier()),
synthesized_small_caps,
)?;
- let font_source = self.font_source.lock();
font.font_key = match font_template.identifier() {
- FontIdentifier::Local(_) => font_source.get_system_font_instance(
+ FontIdentifier::Local(_) => self.system_font_service_proxy.get_system_font_instance(
font_template.identifier(),
font_descriptor.pt_size,
font.webrender_font_instance_flags(),
),
FontIdentifier::Web(_) => self.webrender_font_store.write().get_font_instance(
- &*font_source,
+ self,
font_template.clone(),
font_descriptor.pt_size,
font.webrender_font_instance_flags(),
@@ -236,6 +287,10 @@ impl<S: FontSource> FontContext<S> {
Ok(Arc::new(font))
}
+
+ fn invalidate_font_groups_after_web_font_load(&self) {
+ self.resolved_font_groups.write().clear();
+ }
}
#[derive(Clone)]
@@ -263,7 +318,7 @@ pub trait FontContextWebFontMethods {
-> (Vec<FontKey>, Vec<FontInstanceKey>);
}
-impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontContext<S>> {
+impl<S: SystemFontServiceProxyTrait + 'static> FontContextWebFontMethods for Arc<FontContext<S>> {
fn add_all_web_fonts_from_stylesheet(
&self,
stylesheet: &DocumentStyleSheet,
@@ -325,8 +380,7 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte
name: family_name.name.clone(),
syntax: FontFamilyNameSyntax::Quoted,
});
- self.font_source
- .lock()
+ self.system_font_service_proxy
.find_matching_font_templates(None, &family)
.first()
.cloned()
@@ -373,24 +427,27 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte
RemoteWebFontDownloader::download(url_source, this, web_font_family_name, state)
},
Source::Local(ref local_family_name) => {
- if let Some(new_template) = state
+ if let Some((new_template, font_data)) = state
.local_fonts
.get(&local_family_name.name)
.cloned()
.flatten()
.and_then(|local_template| {
- FontTemplate::new_for_local_web_font(
- local_template,
+ let template = FontTemplate::new_for_local_web_font(
+ local_template.clone(),
&state.css_font_face_descriptors,
state.stylesheet.clone(),
)
- .ok()
+ .ok()?;
+ let font_data = self.get_font_data(&local_template.identifier());
+ Some((template, font_data))
})
{
- let not_cancelled = self
- .web_fonts
- .write()
- .handle_web_font_loaded(&state, new_template);
+ let not_cancelled = self.web_fonts.write().handle_web_font_loaded(
+ &state,
+ new_template,
+ font_data,
+ );
self.handle_web_font_load_finished(&state.finished_callback, not_cancelled);
} else {
this.process_next_web_font_source(state);
@@ -401,8 +458,8 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte
fn remove_all_web_fonts_from_stylesheet(&self, stylesheet: &DocumentStyleSheet) {
let mut web_fonts = self.web_fonts.write();
- let mut fonts = self.cache.fonts.write();
- let mut font_groups = self.cache.resolved_font_groups.write();
+ let mut fonts = self.fonts.write();
+ let mut font_groups = self.resolved_font_groups.write();
// Cancel any currently in-progress web font loads.
web_fonts.handle_stylesheet_removed(stylesheet);
@@ -443,9 +500,9 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte
}
// Lock everything to prevent adding new fonts while we are cleaning up the old ones.
- let web_fonts = self.web_fonts.write();
- let _fonts = self.cache.fonts.write();
- let _font_groups = self.cache.resolved_font_groups.write();
+ let mut web_fonts = self.web_fonts.write();
+ let _fonts = self.fonts.write();
+ let _font_groups = self.resolved_font_groups.write();
let mut webrender_font_store = self.webrender_font_store.write();
let mut unused_identifiers: HashSet<FontIdentifier> = webrender_font_store
@@ -459,13 +516,15 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte
});
}
+ web_fonts.remove_all_font_data_for_identifiers(&unused_identifiers);
+
self.have_removed_web_fonts.store(false, Ordering::Relaxed);
- webrender_font_store.remove_all_fonts_for_identifiers(unused_identifiers)
+ webrender_font_store.remove_all_fonts_for_identifiers(&unused_identifiers)
}
}
-struct RemoteWebFontDownloader<FCT: FontSource> {
- font_context: Arc<FontContext<FCT>>,
+struct RemoteWebFontDownloader<Proxy: SystemFontServiceProxyTrait> {
+ font_context: Arc<FontContext<Proxy>>,
url: ServoArc<Url>,
web_font_family_name: LowercaseFontFamilyName,
response_valid: Mutex<bool>,
@@ -478,10 +537,10 @@ enum DownloaderResponseResult {
Failure,
}
-impl<FCT: FontSource + Send + 'static> RemoteWebFontDownloader<FCT> {
+impl<Proxy: SystemFontServiceProxyTrait + 'static> RemoteWebFontDownloader<Proxy> {
fn download(
url_source: UrlSource,
- font_context: Arc<FontContext<FCT>>,
+ font_context: Arc<FontContext<Proxy>>,
web_font_family_name: LowercaseFontFamilyName,
state: WebFontDownloadState,
) {
@@ -548,7 +607,7 @@ impl<FCT: FontSource + Send + 'static> RemoteWebFontDownloader<FCT> {
);
let font_data = match fontsan::process(&font_data) {
- Ok(bytes) => bytes,
+ Ok(bytes) => Arc::new(FontData::from_bytes(bytes)),
Err(error) => {
debug!(
"Sanitiser rejected web font: family={} url={:?} with {error:?}",
@@ -558,20 +617,28 @@ impl<FCT: FontSource + Send + 'static> RemoteWebFontDownloader<FCT> {
},
};
- let Ok(new_template) = FontTemplate::new_for_remote_web_font(
- self.url.clone().into(),
- Arc::new(font_data),
- &state.css_font_face_descriptors,
- Some(state.stylesheet.clone()),
- ) else {
+ let url: ServoUrl = self.url.clone().into();
+ let identifier = FontIdentifier::Web(url.clone());
+ let Ok(handle) =
+ PlatformFont::new_from_data(identifier, font_data.as_arc().clone(), 0, None)
+ else {
return false;
};
+ let mut descriptor = handle.descriptor();
+ descriptor
+ .override_values_with_css_font_template_descriptors(&state.css_font_face_descriptors);
- let not_cancelled = self
- .font_context
- .web_fonts
- .write()
- .handle_web_font_loaded(state, new_template);
+ let Ok(new_template) =
+ FontTemplate::new_for_remote_web_font(url, descriptor, Some(state.stylesheet.clone()))
+ else {
+ return false;
+ };
+
+ let not_cancelled = self.font_context.web_fonts.write().handle_web_font_loaded(
+ state,
+ new_template,
+ font_data,
+ );
self.font_context
.handle_web_font_load_finished(&state.finished_callback, not_cancelled);
@@ -623,121 +690,12 @@ impl<FCT: FontSource + Send + 'static> RemoteWebFontDownloader<FCT> {
}
}
-#[derive(Default)]
-pub struct CachingFontSource<FCT: FontSource> {
- font_cache_thread: ReentrantMutex<FCT>,
- fonts: RwLock<HashMap<FontCacheKey, Option<FontRef>>>,
- templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
- resolved_font_groups:
- RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>,
-}
-
-impl<FCT: FontSource> CachingFontSource<FCT> {
- fn new(font_cache_thread: FCT) -> Self {
- Self {
- font_cache_thread: ReentrantMutex::new(font_cache_thread),
- fonts: Default::default(),
- templates: Default::default(),
- resolved_font_groups: Default::default(),
- }
- }
-
- fn invalidate_after_web_font_load(&self) {
- self.resolved_font_groups.write().clear();
- }
-
- pub fn matching_templates(
- &self,
- descriptor_to_match: &FontDescriptor,
- family_descriptor: &FontFamilyDescriptor,
- ) -> Vec<FontTemplateRef> {
- let cache_key = FontTemplateCacheKey {
- font_descriptor: descriptor_to_match.clone(),
- family_descriptor: family_descriptor.clone(),
- };
- if let Some(templates) = self.templates.read().get(&cache_key).cloned() {
- return templates;
- }
-
- debug!(
- "CachingFontSource: cache miss for template_descriptor={:?} family_descriptor={:?}",
- descriptor_to_match, family_descriptor
- );
- let templates = self
- .font_cache_thread
- .lock()
- .find_matching_font_templates(Some(descriptor_to_match), &family_descriptor.family);
- self.templates.write().insert(cache_key, templates.clone());
-
- templates
- }
-
- pub fn font_group_with_size(
- &self,
- style: ServoArc<FontStyleStruct>,
- size: Au,
- ) -> Arc<RwLock<FontGroup>> {
- let cache_key = FontGroupCacheKey { size, style };
- if let Some(font_group) = self.resolved_font_groups.read().get(&cache_key) {
- return font_group.clone();
- }
-
- let mut descriptor = FontDescriptor::from(&*cache_key.style);
- descriptor.pt_size = size;
-
- let font_group = Arc::new(RwLock::new(FontGroup::new(&cache_key.style, descriptor)));
- self.resolved_font_groups
- .write()
- .insert(cache_key, font_group.clone());
- font_group
- }
-}
-
-impl<FCT: FontSource> MallocSizeOf for CachingFontSource<FCT> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let font_cache_size = self
- .fonts
- .read()
- .iter()
- .map(|(key, font)| {
- key.size_of(ops) + font.as_ref().map_or(0, |font| (*font).size_of(ops))
- })
- .sum::<usize>();
- let font_template_cache_size = self
- .templates
- .read()
- .iter()
- .map(|(key, templates)| {
- let templates_size = templates
- .iter()
- .map(|template| template.borrow().size_of(ops))
- .sum::<usize>();
- key.size_of(ops) + templates_size
- })
- .sum::<usize>();
- let font_group_cache_size = self
- .resolved_font_groups
- .read()
- .iter()
- .map(|(key, font_group)| key.size_of(ops) + (*font_group.read()).size_of(ops))
- .sum::<usize>();
-
- font_cache_size + font_template_cache_size + font_group_cache_size
- }
-}
-
#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
struct FontCacheKey {
font_identifier: FontIdentifier,
font_descriptor: FontDescriptor,
}
-#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
-struct FontTemplateCacheKey {
- font_descriptor: FontDescriptor,
- family_descriptor: FontFamilyDescriptor,
-}
-
#[derive(Debug, MallocSizeOf)]
struct FontGroupCacheKey {
#[ignore_malloc_size_of = "This is also stored as part of styling."]
diff --git a/components/fonts/font_store.rs b/components/fonts/font_store.rs
index 19e710120d3..e6e5b08ef84 100644
--- a/components/fonts/font_store.rs
+++ b/components/fonts/font_store.rs
@@ -3,25 +3,72 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::collections::{HashMap, HashSet};
-use std::sync::Arc;
+use std::sync::{Arc, OnceLock};
use app_units::Au;
use atomic_refcell::AtomicRefCell;
+use ipc_channel::ipc::IpcSharedMemory;
use log::warn;
use parking_lot::RwLock;
+use serde::{Deserialize, Serialize};
use style::stylesheets::DocumentStyleSheet;
use style::values::computed::{FontStyle, FontWeight};
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
use crate::font::FontDescriptor;
-use crate::font_cache_thread::{FontIdentifier, FontSource, LowercaseFontFamilyName};
use crate::font_context::WebFontDownloadState;
use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods, IsOblique};
+use crate::system_font_service::{
+ FontIdentifier, LowercaseFontFamilyName, SystemFontServiceProxyTrait,
+};
+use crate::FontContext;
+
+/// A data structure to store data for fonts. If sent across IPC channels and only a
+/// [`IpcSharedMemory`] handle is sent, avoiding the overhead of serialization and
+/// deserialization. In addition, if a shared handle to data is requested
+/// (`Arc<Vec<u8>>`), the data is lazily copied out of shared memory once per
+/// [`FontData`].
+#[derive(Debug, Deserialize, Serialize)]
+pub struct FontData {
+ /// The data of this font in shared memory. Suitable for sending across IPC channels.
+ shared_memory: Arc<IpcSharedMemory>,
+ /// A lazily-initialized copy of the data behind an [`Arc`] which can be used when
+ /// passing it to various APIs.
+ #[serde(skip)]
+ arc: OnceLock<Arc<Vec<u8>>>,
+}
+
+impl FontData {
+ pub fn from_bytes(data: Vec<u8>) -> FontData {
+ FontData {
+ shared_memory: Arc::new(IpcSharedMemory::from_bytes(&data)),
+ arc: Arc::new(data).into(),
+ }
+ }
+
+ /// Return a non-shared memory `Arc` view of this data. This may copy the data once
+ /// per [`FontData`], but subsequent calls will return the same shared view.
+ pub fn as_arc(&self) -> &Arc<Vec<u8>> {
+ self.arc
+ .get_or_init(|| Arc::new((**self.shared_memory).into()))
+ }
+
+ /// Return a the [`IpcSharedMemory`] view of this data suitable for sending directly across
+ /// an IPC channel if necessary. An `Arc` is returned to avoid the overhead of copying the
+ /// platform-specific shared memory handle.
+ pub(crate) fn as_ipc_shared_memory(&self) -> Arc<IpcSharedMemory> {
+ self.shared_memory.clone()
+ }
+}
#[derive(Default)]
pub struct FontStore {
pub(crate) families: HashMap<LowercaseFontFamilyName, FontTemplates>,
web_fonts_loading: Vec<(DocumentStyleSheet, usize)>,
+ /// The data for each [`FontIdentifier`]. This data might be used by
+ /// more than one [`FontTemplate`] as each identifier refers to a URL
+ /// or font that can contain more than a single font.
+ font_data: HashMap<FontIdentifier, Arc<FontData>>,
}
pub(crate) type CrossThreadFontStore = Arc<RwLock<FontStore>>;
@@ -78,28 +125,67 @@ impl FontStore {
/// Handle a web font load finishing, adding the new font to the [`FontStore`]. If the web font
/// load was canceled (for instance, if the stylesheet was removed), then do nothing and return
/// false.
+ ///
+ /// In addition pass newly loaded data for this font. Add this data the cached [`FontData`] store
+ /// inside this [`FontStore`].
pub(crate) fn handle_web_font_loaded(
&mut self,
state: &WebFontDownloadState,
new_template: FontTemplate,
+ data: Arc<FontData>,
) -> bool {
// Abort processing this web font if the originating stylesheet was removed.
if self.font_load_cancelled_for_stylesheet(&state.stylesheet) {
return false;
}
-
let family_name = state.css_font_face_descriptors.family_name.clone();
+ self.add_template_and_data(family_name, new_template, data);
+ self.remove_one_web_font_loading_for_stylesheet(&state.stylesheet);
+ true
+ }
+
+ pub(crate) fn add_template_and_data(
+ &mut self,
+ family_name: LowercaseFontFamilyName,
+ new_template: FontTemplate,
+ data: Arc<FontData>,
+ ) {
+ self.font_data.insert(new_template.identifier.clone(), data);
self.families
.entry(family_name)
.or_default()
.add_template(new_template);
- self.remove_one_web_font_loading_for_stylesheet(&state.stylesheet);
- true
}
pub(crate) fn number_of_fonts_still_loading(&self) -> usize {
self.web_fonts_loading.iter().map(|(_, count)| count).sum()
}
+
+ pub(crate) fn get_or_initialize_font_data(
+ &mut self,
+ identifier: &FontIdentifier,
+ ) -> &Arc<FontData> {
+ self.font_data
+ .entry(identifier.clone())
+ .or_insert_with(|| match identifier {
+ FontIdentifier::Local(local_identifier) => {
+ Arc::new(FontData::from_bytes(local_identifier.read_data_from_file()))
+ },
+ FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."),
+ })
+ }
+
+ pub(crate) fn get_font_data(&self, identifier: &FontIdentifier) -> Option<Arc<FontData>> {
+ self.font_data.get(identifier).cloned()
+ }
+
+ pub(crate) fn remove_all_font_data_for_identifiers(
+ &mut self,
+ identifiers: &HashSet<FontIdentifier>,
+ ) {
+ self.font_data
+ .retain(|font_identifier, _| identifiers.contains(font_identifier));
+ }
}
#[derive(Default)]
@@ -110,26 +196,32 @@ pub struct WebRenderFontStore {
pub(crate) type CrossThreadWebRenderFontStore = Arc<RwLock<WebRenderFontStore>>;
impl WebRenderFontStore {
- pub(crate) fn get_font_instance<FCT: FontSource>(
+ pub(crate) fn get_font_instance<Proxy: SystemFontServiceProxyTrait>(
&mut self,
- font_cache_thread: &FCT,
+ font_context: &FontContext<Proxy>,
font_template: FontTemplateRef,
pt_size: Au,
flags: FontInstanceFlags,
) -> FontInstanceKey {
let webrender_font_key_map = &mut self.webrender_font_key_map;
let identifier = font_template.identifier().clone();
+
let font_key = *webrender_font_key_map
.entry(identifier.clone())
.or_insert_with(|| {
- font_cache_thread.get_web_font(font_template.data(), identifier.index())
+ let data = font_context.get_font_data(&identifier);
+ font_context
+ .system_font_service_proxy
+ .get_web_font(data, identifier.index())
});
*self
.webrender_font_instance_map
.entry((font_key, pt_size))
.or_insert_with(|| {
- font_cache_thread.get_web_font_instance(font_key, pt_size.to_f32_px(), flags)
+ font_context
+ .system_font_service_proxy
+ .get_web_font_instance(font_key, pt_size.to_f32_px(), flags)
})
}
@@ -148,7 +240,7 @@ impl WebRenderFontStore {
pub(crate) fn remove_all_fonts_for_identifiers(
&mut self,
- identifiers: HashSet<FontIdentifier>,
+ identifiers: &HashSet<FontIdentifier>,
) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
let mut removed_keys: HashSet<FontKey> = HashSet::new();
self.webrender_font_key_map.retain(|identifier, font_key| {
diff --git a/components/fonts/font_template.rs b/components/fonts/font_template.rs
index 19c71ed83fa..d053fe9f2d4 100644
--- a/components/fonts/font_template.rs
+++ b/components/fonts/font_template.rs
@@ -15,12 +15,11 @@ use style::computed_values::font_style::T as FontStyle;
use style::stylesheets::DocumentStyleSheet;
use style::values::computed::font::FontWeight;
-use crate::font::{FontDescriptor, PlatformFontMethods};
-use crate::font_cache_thread::{
+use crate::font::FontDescriptor;
+use crate::platform::font_list::LocalFontIdentifier;
+use crate::system_font_service::{
CSSFontFaceDescriptors, ComputedFontStyleDescriptor, FontIdentifier,
};
-use crate::platform::font::PlatformFont;
-use crate::platform::font_list::LocalFontIdentifier;
/// A reference to a [`FontTemplate`] with shared ownership and mutability.
pub type FontTemplateRef = Arc<AtomicRefCell<FontTemplate>>;
@@ -109,7 +108,7 @@ impl FontTemplateDescriptor {
self.stretch.1 >= descriptor_to_match.stretch
}
- fn override_values_with_css_font_template_descriptors(
+ pub(crate) fn override_values_with_css_font_template_descriptors(
&mut self,
css_font_template_descriptors: &CSSFontFaceDescriptors,
) {
@@ -137,27 +136,20 @@ impl FontTemplateDescriptor {
/// This describes all the information needed to create
/// font instance handles. It contains a unique
/// FontTemplateData structure that is platform specific.
-#[derive(Clone)]
+#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
pub struct FontTemplate {
pub identifier: FontIdentifier,
pub descriptor: FontTemplateDescriptor,
- /// The data to use for this [`FontTemplate`]. For web fonts, this is always filled, but
- /// for local fonts, this is loaded only lazily in layout.
- pub data: Option<Arc<Vec<u8>>>,
/// If this font is a web font, this is a reference to the stylesheet that
/// created it. This will be used to remove this font from caches, when the
/// stylesheet is removed.
+ ///
+ /// This is not serialized, as it's only useful in the [`super::FontContext`]
+ /// that it is created in.
+ #[serde(skip)]
pub stylesheet: Option<DocumentStyleSheet>,
}
-impl malloc_size_of::MallocSizeOf for FontTemplate {
- fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
- self.identifier.size_of(ops) +
- self.descriptor.size_of(ops) +
- self.data.as_ref().map_or(0, |data| (*data).size_of(ops))
- }
-}
-
impl Debug for FontTemplate {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
self.identifier.fmt(f)
@@ -176,7 +168,6 @@ impl FontTemplate {
FontTemplate {
identifier: FontIdentifier::Local(identifier),
descriptor,
- data: None,
stylesheet: None,
}
}
@@ -184,22 +175,12 @@ impl FontTemplate {
/// Create a new [`FontTemplate`] for a `@font-family` with a `url(...)` `src` font.
pub fn new_for_remote_web_font(
url: ServoUrl,
- data: Arc<Vec<u8>>,
- css_font_template_descriptors: &CSSFontFaceDescriptors,
+ descriptor: FontTemplateDescriptor,
stylesheet: Option<DocumentStyleSheet>,
) -> Result<FontTemplate, &'static str> {
- let identifier = FontIdentifier::Web(url.clone());
- let Ok(handle) = PlatformFont::new_from_data(identifier, data.clone(), 0, None) else {
- return Err("Could not initialize platform font data for: {url:?}");
- };
-
- let mut descriptor = handle.descriptor();
- descriptor
- .override_values_with_css_font_template_descriptors(css_font_template_descriptors);
Ok(FontTemplate {
identifier: FontIdentifier::Web(url),
descriptor,
- data: Some(data),
stylesheet,
})
}
@@ -223,19 +204,9 @@ impl FontTemplate {
pub fn identifier(&self) -> &FontIdentifier {
&self.identifier
}
-
- /// Returns a reference to the bytes in this font if they are in memory.
- /// This function never performs disk I/O.
- pub fn data_if_in_memory(&self) -> Option<Arc<Vec<u8>>> {
- self.data.clone()
- }
}
pub trait FontTemplateRefMethods {
- /// Returns a reference to the data in this font. This may be a hugely expensive
- /// operation (depending on the platform) which performs synchronous disk I/O
- /// and should never be done lightly.
- fn data(&self) -> Arc<Vec<u8>>;
/// Get the descriptor.
fn descriptor(&self) -> FontTemplateDescriptor;
/// Get the [`FontIdentifier`] for this template.
@@ -267,24 +238,6 @@ impl FontTemplateRefMethods for FontTemplateRef {
self.descriptor().distance_from(descriptor_to_match)
}
- fn data(&self) -> Arc<Vec<u8>> {
- if let Some(data) = self.borrow().data.clone() {
- return data;
- }
-
- let mut template = self.borrow_mut();
- let identifier = template.identifier.clone();
- template
- .data
- .get_or_insert_with(|| match identifier {
- FontIdentifier::Local(local_identifier) => {
- Arc::new(local_identifier.read_data_from_file())
- },
- FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."),
- })
- .clone()
- }
-
fn char_in_unicode_range(&self, character: char) -> bool {
let character = character as u32;
self.borrow()
diff --git a/components/fonts/lib.rs b/components/fonts/lib.rs
index 305db5e53ef..429672f3616 100644
--- a/components/fonts/lib.rs
+++ b/components/fonts/lib.rs
@@ -5,7 +5,6 @@
#![deny(unsafe_code)]
mod font;
-mod font_cache_thread;
mod font_context;
mod font_store;
mod font_template;
@@ -13,14 +12,15 @@ mod glyph;
#[allow(unsafe_code)]
pub mod platform;
mod shaper;
+mod system_font_service;
pub use font::*;
-pub use font_cache_thread::*;
pub use font_context::*;
pub use font_store::*;
pub use font_template::*;
pub use glyph::*;
pub use shaper::*;
+pub use system_font_service::*;
use unicode_properties::{emoji, EmojiStatus, UnicodeEmoji};
/// Whether or not font fallback selection prefers the emoji or text representation
diff --git a/components/fonts/platform/freetype/android/font_list.rs b/components/fonts/platform/freetype/android/font_list.rs
index 7f599041ce3..c52b06d8b03 100644
--- a/components/fonts/platform/freetype/android/font_list.rs
+++ b/components/fonts/platform/freetype/android/font_list.rs
@@ -452,7 +452,7 @@ impl FontList {
}
}
-// Functions used by FontCacheThread
+// Functions used by SystemFontSerivce
pub fn for_each_available_family<F>(mut callback: F)
where
F: FnMut(String),
diff --git a/components/fonts/platform/freetype/font.rs b/components/fonts/platform/freetype/font.rs
index 3a77ab2d8d9..43885556942 100644
--- a/components/fonts/platform/freetype/font.rs
+++ b/components/fonts/platform/freetype/font.rs
@@ -29,9 +29,9 @@ use crate::font::{
FontMetrics, FontTableMethods, FontTableTag, FractionalPixel, PlatformFontMethods, GPOS, GSUB,
KERN,
};
-use crate::font_cache_thread::FontIdentifier;
use crate::font_template::FontTemplateDescriptor;
use crate::glyph::GlyphId;
+use crate::system_font_service::FontIdentifier;
// This constant is not present in the freetype
// bindings due to bindgen not handling the way
diff --git a/components/fonts/platform/freetype/ohos/font_list.rs b/components/fonts/platform/freetype/ohos/font_list.rs
index bc8303b78aa..82a5740f124 100644
--- a/components/fonts/platform/freetype/ohos/font_list.rs
+++ b/components/fonts/platform/freetype/ohos/font_list.rs
@@ -453,7 +453,7 @@ impl FontList {
}
}
-// Functions used by FontCacheThread
+// Functions used by SystemFontService
pub fn for_each_available_family<F>(mut callback: F)
where
F: FnMut(String),
diff --git a/components/fonts/platform/macos/core_text_font_cache.rs b/components/fonts/platform/macos/core_text_font_cache.rs
index 6f41a25ce70..0574ff78325 100644
--- a/components/fonts/platform/macos/core_text_font_cache.rs
+++ b/components/fonts/platform/macos/core_text_font_cache.rs
@@ -16,7 +16,7 @@ use core_text::font::CTFont;
use core_text::font_descriptor::kCTFontURLAttribute;
use parking_lot::RwLock;
-use crate::font_cache_thread::FontIdentifier;
+use crate::system_font_service::FontIdentifier;
/// A cache of `CTFont` to avoid having to create `CTFont` instances over and over. It is
/// always possible to create a `CTFont` using a `FontTemplate` even if it isn't in this
diff --git a/components/fonts/font_cache_thread.rs b/components/fonts/system_font_service.rs
index 5d5f3cff4b7..8bf15eaa2eb 100644
--- a/components/fonts/font_cache_thread.rs
+++ b/components/fonts/system_font_service.rs
@@ -11,9 +11,10 @@ use std::{fmt, thread};
use app_units::Au;
use atomic_refcell::AtomicRefCell;
-use ipc_channel::ipc::{self, IpcBytesReceiver, IpcBytesSender, IpcReceiver, IpcSender};
+use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use log::debug;
use malloc_size_of_derive::MallocSizeOf;
+use parking_lot::{ReentrantMutex, RwLock};
use serde::{Deserialize, Serialize};
use servo_config::pref;
use servo_url::ServoUrl;
@@ -29,13 +30,12 @@ use webrender_traits::WebRenderFontApi;
use crate::font::FontDescriptor;
use crate::font_store::FontStore;
-use crate::font_template::{
- FontTemplate, FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods,
-};
+use crate::font_template::{FontTemplate, FontTemplateRef};
use crate::platform::font_list::{
default_system_generic_font_family, for_each_available_family, for_each_variation,
LocalFontIdentifier,
};
+use crate::FontData;
#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub enum FontIdentifier {
@@ -53,19 +53,18 @@ impl FontIdentifier {
}
#[derive(Debug, Deserialize, Serialize)]
-pub struct SerializedFontTemplate {
- identifier: FontIdentifier,
- descriptor: FontTemplateDescriptor,
- bytes_receiver: ipc_channel::ipc::IpcBytesReceiver,
+pub struct FontTemplateRequestResult {
+ templates: Vec<FontTemplate>,
+ template_data: Vec<(FontIdentifier, Arc<FontData>)>,
}
-/// Commands that the FontContext sends to the font cache thread.
+/// Commands that the `FontContext` sends to the `SystemFontService`.
#[derive(Debug, Deserialize, Serialize)]
pub enum Command {
GetFontTemplates(
Option<FontDescriptor>,
SingleFontFamily,
- IpcSender<Vec<SerializedFontTemplate>>,
+ IpcSender<FontTemplateRequestResult>,
),
GetFontInstance(
FontIdentifier,
@@ -73,7 +72,7 @@ pub enum Command {
FontInstanceFlags,
IpcSender<FontInstanceKey>,
),
- GetWebFont(IpcBytesReceiver, u32, IpcSender<FontKey>),
+ GetWebFont(Arc<FontData>, u32, IpcSender<FontKey>),
GetWebFontInstance(FontKey, f32, FontInstanceFlags, IpcSender<FontInstanceKey>),
Exit(IpcSender<()>),
Ping,
@@ -90,11 +89,11 @@ struct ResolvedGenericFontFamilies {
system_ui: OnceCell<LowercaseFontFamilyName>,
}
-/// The font cache thread itself. It maintains a list of reference counted
-/// font templates that are currently in use.
-struct FontCache {
+/// The system font service. There is one of these for every Servo instance. This is a thread,
+/// responsible for reading the list of system fonts, handling requests to match against
+/// them, and ensuring that only one copy of system font data is loaded at a time.
+pub struct SystemFontService {
port: IpcReceiver<Command>,
- font_data: HashMap<FontIdentifier, Arc<Vec<u8>>>,
local_families: FontStore,
webrender_api: Box<dyn WebRenderFontApi>,
webrender_fonts: HashMap<FontIdentifier, FontKey>,
@@ -102,68 +101,62 @@ struct FontCache {
generic_fonts: ResolvedGenericFontFamilies,
}
-impl FontCache {
+#[derive(Clone, Deserialize, Serialize)]
+pub struct SystemFontServiceProxySender(IpcSender<Command>);
+
+impl SystemFontServiceProxySender {
+ pub fn to_proxy(&self) -> SystemFontServiceProxy {
+ SystemFontServiceProxy {
+ sender: ReentrantMutex::new(self.0.clone()),
+ templates: Default::default(),
+ data_cache: Default::default(),
+ }
+ }
+}
+
+impl SystemFontService {
+ pub fn spawn(webrender_api: Box<dyn WebRenderFontApi + Send>) -> SystemFontServiceProxySender {
+ let (sender, receiver) = ipc::channel().unwrap();
+
+ thread::Builder::new()
+ .name("SystemFontService".to_owned())
+ .spawn(move || {
+ #[allow(clippy::default_constructed_unit_structs)]
+ let mut cache = SystemFontService {
+ port: receiver,
+ local_families: Default::default(),
+ webrender_api,
+ webrender_fonts: HashMap::new(),
+ font_instances: HashMap::new(),
+ generic_fonts: Default::default(),
+ };
+
+ cache.refresh_local_families();
+ cache.run();
+ })
+ .expect("Thread spawning failed");
+
+ SystemFontServiceProxySender(sender)
+ }
+
#[tracing::instrument(skip(self), fields(servo_profiling = true))]
fn run(&mut self) {
loop {
let msg = self.port.recv().unwrap();
match msg {
- Command::GetFontTemplates(descriptor_to_match, font_family, result) => {
- let span = span!(
- Level::TRACE,
- "Command::GetFontTemplates",
- servo_profiling = true
- );
+ Command::GetFontTemplates(font_descriptor, font_family, result_sender) => {
+ let span = span!(Level::TRACE, "GetFontTemplates", servo_profiling = true);
let _span = span.enter();
- let templates =
- self.find_font_templates(descriptor_to_match.as_ref(), &font_family);
- debug!("Found templates for descriptor {descriptor_to_match:?}: ");
- debug!(" {templates:?}");
-
- let (serialized_templates, senders): (
- Vec<SerializedFontTemplate>,
- Vec<(FontTemplateRef, IpcBytesSender)>,
- ) = templates
- .into_iter()
- .map(|template| {
- let (bytes_sender, bytes_receiver) =
- ipc::bytes_channel().expect("failed to create IPC channel");
- (
- SerializedFontTemplate {
- identifier: template.identifier().clone(),
- descriptor: template.descriptor().clone(),
- bytes_receiver,
- },
- (template.clone(), bytes_sender),
- )
- })
- .unzip();
-
- let _ = result.send(serialized_templates);
-
- // NB: This will load the font into memory if it hasn't been loaded already.
- for (font_template, bytes_sender) in senders.iter() {
- let identifier = font_template.identifier();
- let data = self
- .font_data
- .entry(identifier)
- .or_insert_with(|| font_template.data());
- let span = span!(
- Level::TRACE,
- "GetFontTemplates send",
- servo_profiling = true
- );
- let _span = span.enter();
- let _ = bytes_sender.send(data);
- }
+ let _ =
+ result_sender.send(self.get_font_templates(font_descriptor, font_family));
},
Command::GetFontInstance(identifier, pt_size, flags, result) => {
let _ = result.send(self.get_font_instance(identifier, pt_size, flags));
},
- Command::GetWebFont(bytes_receiver, font_index, result_sender) => {
+ Command::GetWebFont(data, font_index, result_sender) => {
self.webrender_api.forward_add_font_message(
- bytes_receiver,
+ data.as_ipc_shared_memory(),
font_index,
result_sender,
);
@@ -190,6 +183,38 @@ impl FontCache {
}
}
+ fn get_font_templates(
+ &mut self,
+ font_descriptor: Option<FontDescriptor>,
+ font_family: SingleFontFamily,
+ ) -> FontTemplateRequestResult {
+ let templates = self.find_font_templates(font_descriptor.as_ref(), &font_family);
+ let templates: Vec<_> = templates
+ .into_iter()
+ .map(|template| template.borrow().clone())
+ .collect();
+
+ // The `FontData` for all templates is also sent along with the `FontTemplate`s. This is to ensure that
+ // the data is not read from disk in each content process. The data is loaded once here in the system
+ // font service and each process gets another handle to the `IpcSharedMemory` view of that data.
+ let template_data = templates
+ .iter()
+ .map(|template| {
+ let identifier = template.identifier.clone();
+ let data = self
+ .local_families
+ .get_or_initialize_font_data(&identifier)
+ .clone();
+ (identifier, data)
+ })
+ .collect();
+
+ FontTemplateRequestResult {
+ templates,
+ template_data,
+ }
+ }
+
fn refresh_local_families(&mut self) {
self.local_families.clear();
for_each_available_family(|family_name| {
@@ -232,11 +257,7 @@ impl FontCache {
) -> FontInstanceKey {
let webrender_font_api = &self.webrender_api;
let webrender_fonts = &mut self.webrender_fonts;
- let font_data = self
- .font_data
- .get(&identifier)
- .expect("Got unexpected FontIdentifier")
- .clone();
+ let font_data = self.local_families.get_or_initialize_font_data(&identifier);
let font_key = *webrender_fonts
.entry(identifier.clone())
@@ -252,7 +273,7 @@ impl FontCache {
.add_system_font(local_font_identifier.native_font_handle());
}
- webrender_font_api.add_font(font_data, identifier.index())
+ webrender_font_api.add_font(font_data.as_ipc_shared_memory(), identifier.index())
});
*self
@@ -304,7 +325,8 @@ impl FontCache {
}
}
-pub trait FontSource: Clone {
+/// A trait for accessing the [`SystemFontServiceProxy`] necessary for unit testing.
+pub trait SystemFontServiceProxyTrait: Send + Sync {
fn find_matching_font_templates(
&self,
descriptor_to_match: Option<&FontDescriptor>,
@@ -316,20 +338,29 @@ pub trait FontSource: Clone {
size: Au,
flags: FontInstanceFlags,
) -> FontInstanceKey;
- fn get_web_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey;
+ fn get_web_font(&self, data: Arc<FontData>, index: u32) -> FontKey;
fn get_web_font_instance(
&self,
font_key: FontKey,
size: f32,
flags: FontInstanceFlags,
) -> FontInstanceKey;
+ fn get_font_data(&self, identifier: &FontIdentifier) -> Option<Arc<FontData>>;
}
-/// The public interface to the font cache thread, used by per-thread `FontContext` instances (via
-/// the `FontSource` trait), and also by layout.
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct FontCacheThread {
- chan: IpcSender<Command>,
+#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
+struct FontTemplateCacheKey {
+ font_descriptor: Option<FontDescriptor>,
+ family_descriptor: SingleFontFamily,
+}
+
+/// The public interface to the [`SystemFontService`], used by per-Document `FontContext`
+/// instances (via [`SystemFontServiceProxyTrait`]).
+#[derive(Debug)]
+pub struct SystemFontServiceProxy {
+ sender: ReentrantMutex<IpcSender<Command>>,
+ templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
+ data_cache: RwLock<HashMap<FontIdentifier, Arc<FontData>>>,
}
/// A version of `FontStyle` from Stylo that is serializable. Normally this is not
@@ -417,44 +448,24 @@ impl From<&FontFaceRuleData> for CSSFontFaceDescriptors {
}
}
-impl FontCacheThread {
- pub fn new(webrender_api: Box<dyn WebRenderFontApi + Send>) -> FontCacheThread {
- let (chan, port) = ipc::channel().unwrap();
-
- thread::Builder::new()
- .name("FontCache".to_owned())
- .spawn(move || {
- #[allow(clippy::default_constructed_unit_structs)]
- let mut cache = FontCache {
- port,
- font_data: HashMap::new(),
- local_families: Default::default(),
- webrender_api,
- webrender_fonts: HashMap::new(),
- font_instances: HashMap::new(),
- generic_fonts: Default::default(),
- };
-
- cache.refresh_local_families();
- cache.run();
- })
- .expect("Thread spawning failed");
-
- FontCacheThread { chan }
- }
-
+impl SystemFontServiceProxy {
pub fn exit(&self) {
let (response_chan, response_port) = ipc::channel().unwrap();
- self.chan
+ self.sender
+ .lock()
.send(Command::Exit(response_chan))
- .expect("Couldn't send FontCacheThread exit message");
+ .expect("Couldn't send SystemFontService exit message");
response_port
.recv()
- .expect("Couldn't receive FontCacheThread reply");
+ .expect("Couldn't receive SystemFontService reply");
+ }
+
+ pub fn to_sender(&self) -> SystemFontServiceProxySender {
+ SystemFontServiceProxySender(self.sender.lock().clone())
}
}
-impl FontSource for FontCacheThread {
+impl SystemFontServiceProxyTrait for SystemFontServiceProxy {
fn get_system_font_instance(
&self,
identifier: FontIdentifier,
@@ -462,23 +473,24 @@ impl FontSource for FontCacheThread {
flags: FontInstanceFlags,
) -> FontInstanceKey {
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
- self.chan
+ self.sender
+ .lock()
.send(Command::GetFontInstance(
identifier,
size,
flags,
response_chan,
))
- .expect("failed to send message to font cache thread");
+ .expect("failed to send message to system font service");
let instance_key = response_port.recv();
if instance_key.is_err() {
- let font_thread_has_closed = self.chan.send(Command::Ping).is_err();
+ let font_thread_has_closed = self.sender.lock().send(Command::Ping).is_err();
assert!(
font_thread_has_closed,
"Failed to receive a response from live font cache"
);
- panic!("Font cache thread has already exited.");
+ panic!("SystemFontService has already exited.");
}
instance_key.unwrap()
}
@@ -486,51 +498,59 @@ impl FontSource for FontCacheThread {
fn find_matching_font_templates(
&self,
descriptor_to_match: Option<&FontDescriptor>,
- font_family: &SingleFontFamily,
+ family_descriptor: &SingleFontFamily,
) -> Vec<FontTemplateRef> {
+ let cache_key = FontTemplateCacheKey {
+ font_descriptor: descriptor_to_match.cloned(),
+ family_descriptor: family_descriptor.clone(),
+ };
+ if let Some(templates) = self.templates.read().get(&cache_key).cloned() {
+ return templates;
+ }
+
+ debug!(
+ "SystemFontServiceProxy: cache miss for template_descriptor={:?} family_descriptor={:?}",
+ descriptor_to_match, family_descriptor
+ );
+
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
- self.chan
+ self.sender
+ .lock()
.send(Command::GetFontTemplates(
descriptor_to_match.cloned(),
- font_family.clone(),
+ family_descriptor.clone(),
response_chan,
))
- .expect("failed to send message to font cache thread");
+ .expect("failed to send message to system font service");
let reply = response_port.recv();
- if reply.is_err() {
- let font_thread_has_closed = self.chan.send(Command::Ping).is_err();
+ let Ok(reply) = reply else {
+ let font_thread_has_closed = self.sender.lock().send(Command::Ping).is_err();
assert!(
font_thread_has_closed,
"Failed to receive a response from live font cache"
);
- panic!("Font cache thread has already exited.");
- }
+ panic!("SystemFontService has already exited.");
+ };
- reply
- .unwrap()
+ let templates: Vec<_> = reply
+ .templates
.into_iter()
- .map(|serialized_font_template| {
- let font_data = serialized_font_template.bytes_receiver.recv().ok();
- Arc::new(AtomicRefCell::new(FontTemplate {
- identifier: serialized_font_template.identifier,
- descriptor: serialized_font_template.descriptor.clone(),
- data: font_data.map(Arc::new),
- stylesheet: None,
- }))
- })
- .collect()
+ .map(AtomicRefCell::new)
+ .map(Arc::new)
+ .collect();
+ self.data_cache.write().extend(reply.template_data);
+
+ templates
}
- fn get_web_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey {
+ fn get_web_font(&self, data: Arc<FontData>, index: u32) -> FontKey {
let (result_sender, result_receiver) =
ipc::channel().expect("failed to create IPC channel");
- let (bytes_sender, bytes_receiver) =
- ipc::bytes_channel().expect("failed to create IPC channel");
let _ = self
- .chan
- .send(Command::GetWebFont(bytes_receiver, index, result_sender));
- let _ = bytes_sender.send(&data);
+ .sender
+ .lock()
+ .send(Command::GetWebFont(data, index, result_sender));
result_receiver.recv().unwrap()
}
@@ -542,7 +562,7 @@ impl FontSource for FontCacheThread {
) -> FontInstanceKey {
let (result_sender, result_receiver) =
ipc::channel().expect("failed to create IPC channel");
- let _ = self.chan.send(Command::GetWebFontInstance(
+ let _ = self.sender.lock().send(Command::GetWebFontInstance(
font_key,
font_size,
font_flags,
@@ -550,6 +570,10 @@ impl FontSource for FontCacheThread {
));
result_receiver.recv().unwrap()
}
+
+ fn get_font_data(&self, identifier: &FontIdentifier) -> Option<Arc<FontData>> {
+ self.data_cache.read().get(identifier).cloned()
+ }
}
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
diff --git a/components/fonts/tests/font_context.rs b/components/fonts/tests/font_context.rs
index 91eddaeccc3..470155a2871 100644
--- a/components/fonts/tests/font_context.rs
+++ b/components/fonts/tests/font_context.rs
@@ -2,22 +2,24 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
-use std::rc::Rc;
+use std::sync::atomic::{AtomicI32, Ordering};
+use std::sync::Arc;
use app_units::Au;
+use fonts::platform::font::PlatformFont;
use fonts::{
- fallback_font_families, CSSFontFaceDescriptors, FallbackFontSelectionOptions, FontContext,
- FontDescriptor, FontFamilyDescriptor, FontIdentifier, FontSearchScope, FontSource,
- FontTemplate, FontTemplateRef, FontTemplates,
+ fallback_font_families, FallbackFontSelectionOptions, FontContext, FontData, FontDescriptor,
+ FontFamilyDescriptor, FontIdentifier, FontSearchScope, FontTemplate, FontTemplateRef,
+ FontTemplates, PlatformFontMethods, SystemFontServiceProxyTrait,
};
use ipc_channel::ipc;
use net_traits::ResourceThreads;
-use servo_arc::Arc;
+use parking_lot::Mutex;
+use servo_arc::Arc as ServoArc;
use servo_atoms::Atom;
use servo_url::ServoUrl;
use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps;
@@ -31,35 +33,40 @@ use style::values::generics::font::LineHeight;
use style::ArcSlice;
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, IdNamespace};
-#[derive(Clone)]
struct MockFontCacheThread {
- families: RefCell<HashMap<String, FontTemplates>>,
- find_font_count: Rc<Cell<isize>>,
+ families: Mutex<HashMap<String, FontTemplates>>,
+ data: Mutex<HashMap<FontIdentifier, Arc<FontData>>>,
+ find_font_count: AtomicI32,
}
impl MockFontCacheThread {
- fn new() -> MockFontCacheThread {
+ fn new() -> Self {
+ let proxy = Self {
+ families: Default::default(),
+ data: Default::default(),
+ find_font_count: AtomicI32::new(0),
+ };
+
let mut csstest_ascii = FontTemplates::default();
- Self::add_face(&mut csstest_ascii, "csstest-ascii");
+ proxy.add_face(&mut csstest_ascii, "csstest-ascii");
let mut csstest_basic = FontTemplates::default();
- Self::add_face(&mut csstest_basic, "csstest-basic-regular");
+ proxy.add_face(&mut csstest_basic, "csstest-basic-regular");
let mut fallback = FontTemplates::default();
- Self::add_face(&mut fallback, "csstest-basic-regular");
-
- let mut families = HashMap::new();
- families.insert("CSSTest ASCII".to_owned(), csstest_ascii);
- families.insert("CSSTest Basic".to_owned(), csstest_basic);
- families.insert(
- fallback_font_families(FallbackFontSelectionOptions::default())[0].to_owned(),
- fallback,
- );
-
- MockFontCacheThread {
- families: RefCell::new(families),
- find_font_count: Rc::new(Cell::new(0)),
+ proxy.add_face(&mut fallback, "csstest-basic-regular");
+
+ {
+ let mut families = proxy.families.lock();
+ families.insert("CSSTest ASCII".to_owned(), csstest_ascii);
+ families.insert("CSSTest Basic".to_owned(), csstest_basic);
+ families.insert(
+ fallback_font_families(FallbackFontSelectionOptions::default())[0].to_owned(),
+ fallback,
+ );
}
+
+ proxy
}
fn identifier_for_font_name(name: &str) -> FontIdentifier {
@@ -74,39 +81,44 @@ impl MockFontCacheThread {
ServoUrl::from_file_path(path).unwrap()
}
- fn add_face(family: &mut FontTemplates, name: &str) {
+ fn add_face(&self, family: &mut FontTemplates, name: &str) {
let mut path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "tests", "support", "CSSTest"]
.iter()
.collect();
path.push(format!("{}.ttf", name));
let file = File::open(path).unwrap();
- let data: Vec<u8> = file.bytes().map(|b| b.unwrap()).collect();
- family.add_template(
- FontTemplate::new_for_remote_web_font(
- Self::url_for_font_name(name),
- std::sync::Arc::new(data),
- &CSSFontFaceDescriptors::new(name),
- None,
- )
- .unwrap(),
- );
+ let data = Arc::new(FontData::from_bytes(
+ file.bytes().map(|b| b.unwrap()).collect(),
+ ));
+
+ let url = Self::url_for_font_name(name);
+ let identifier = FontIdentifier::Web(url.clone());
+ let handle =
+ PlatformFont::new_from_data(identifier.clone(), data.as_arc().clone(), 0, None)
+ .expect("Could not load test font");
+ let template =
+ FontTemplate::new_for_remote_web_font(url, handle.descriptor(), None).unwrap();
+ family.add_template(template);
+
+ self.data.lock().insert(identifier, data);
}
}
-impl FontSource for MockFontCacheThread {
+impl SystemFontServiceProxyTrait for MockFontCacheThread {
fn find_matching_font_templates(
&self,
descriptor_to_match: Option<&FontDescriptor>,
font_family: &SingleFontFamily,
) -> Vec<FontTemplateRef> {
- self.find_font_count.set(self.find_font_count.get() + 1);
+ self.find_font_count.fetch_add(1, Ordering::Relaxed);
+
let SingleFontFamily::FamilyName(family_name) = font_family else {
return Vec::new();
};
self.families
- .borrow_mut()
+ .lock()
.get_mut(&*family_name.name)
.map(|family| family.find_for_descriptor(descriptor_to_match))
.unwrap_or_default()
@@ -114,25 +126,29 @@ impl FontSource for MockFontCacheThread {
fn get_system_font_instance(
&self,
- _: FontIdentifier,
- _: Au,
- _: FontInstanceFlags,
+ _font_identifier: FontIdentifier,
+ _size: Au,
+ _flags: FontInstanceFlags,
) -> FontInstanceKey {
FontInstanceKey(IdNamespace(0), 0)
}
- fn get_web_font(&self, _: std::sync::Arc<Vec<u8>>, _: u32) -> webrender_api::FontKey {
+ fn get_web_font(&self, _data: Arc<fonts::FontData>, _index: u32) -> FontKey {
FontKey(IdNamespace(0), 0)
}
fn get_web_font_instance(
&self,
- _: webrender_api::FontKey,
- _: f32,
- _: FontInstanceFlags,
+ _font_key: FontKey,
+ _size: f32,
+ _flags: FontInstanceFlags,
) -> FontInstanceKey {
FontInstanceKey(IdNamespace(0), 0)
}
+
+ fn get_font_data(&self, identifier: &FontIdentifier) -> Option<Arc<fonts::FontData>> {
+ self.data.lock().get(identifier).cloned()
+ }
}
fn style() -> FontStyleStruct {
@@ -177,7 +193,7 @@ fn mock_resource_threads() -> ResourceThreads {
#[test]
fn test_font_group_is_cached_by_style() {
- let source = MockFontCacheThread::new();
+ let source = Arc::new(MockFontCacheThread::new());
let context = FontContext::new(source, mock_resource_threads());
let style1 = style();
@@ -187,16 +203,16 @@ fn test_font_group_is_cached_by_style() {
assert!(
std::ptr::eq(
- &*context.font_group(Arc::new(style1.clone())).read(),
- &*context.font_group(Arc::new(style1.clone())).read()
+ &*context.font_group(ServoArc::new(style1.clone())).read(),
+ &*context.font_group(ServoArc::new(style1.clone())).read()
),
"the same font group should be returned for two styles with the same hash"
);
assert!(
!std::ptr::eq(
- &*context.font_group(Arc::new(style1.clone())).read(),
- &*context.font_group(Arc::new(style2.clone())).read()
+ &*context.font_group(ServoArc::new(style1.clone())).read(),
+ &*context.font_group(ServoArc::new(style2.clone())).read()
),
"different font groups should be returned for two styles with different hashes"
)
@@ -204,14 +220,13 @@ fn test_font_group_is_cached_by_style() {
#[test]
fn test_font_group_find_by_codepoint() {
- let source = MockFontCacheThread::new();
- let count = source.find_font_count.clone();
- let mut context = FontContext::new(source, mock_resource_threads());
+ let source = Arc::new(MockFontCacheThread::new());
+ let mut context = FontContext::new(source.clone(), mock_resource_threads());
let mut style = style();
style.set_font_family(font_family(vec!["CSSTest ASCII", "CSSTest Basic"]));
- let group = context.font_group(Arc::new(style));
+ let group = context.font_group(ServoArc::new(style));
let font = group
.write()
@@ -222,7 +237,7 @@ fn test_font_group_find_by_codepoint() {
MockFontCacheThread::identifier_for_font_name("csstest-ascii")
);
assert_eq!(
- count.get(),
+ source.find_font_count.fetch_add(0, Ordering::Relaxed),
1,
"only the first font in the list should have been loaded"
);
@@ -236,7 +251,7 @@ fn test_font_group_find_by_codepoint() {
MockFontCacheThread::identifier_for_font_name("csstest-ascii")
);
assert_eq!(
- count.get(),
+ source.find_font_count.fetch_add(0, Ordering::Relaxed),
1,
"we shouldn't load the same font a second time"
);
@@ -249,18 +264,22 @@ fn test_font_group_find_by_codepoint() {
font.identifier(),
MockFontCacheThread::identifier_for_font_name("csstest-basic-regular")
);
- assert_eq!(count.get(), 2, "both fonts should now have been loaded");
+ assert_eq!(
+ source.find_font_count.fetch_add(0, Ordering::Relaxed),
+ 2,
+ "both fonts should now have been loaded"
+ );
}
#[test]
fn test_font_fallback() {
- let source = MockFontCacheThread::new();
+ let source = Arc::new(MockFontCacheThread::new());
let mut context = FontContext::new(source, mock_resource_threads());
let mut style = style();
style.set_font_family(font_family(vec!["CSSTest ASCII"]));
- let group = context.font_group(Arc::new(style));
+ let group = context.font_group(ServoArc::new(style));
let font = group
.write()
@@ -285,9 +304,8 @@ fn test_font_fallback() {
#[test]
fn test_font_template_is_cached() {
- let source = MockFontCacheThread::new();
- let count = source.find_font_count.clone();
- let context = FontContext::new(source, mock_resource_threads());
+ let source = Arc::new(MockFontCacheThread::new());
+ let context = FontContext::new(source.clone(), mock_resource_threads());
let mut font_descriptor = FontDescriptor {
weight: FontWeight::normal(),
@@ -320,7 +338,7 @@ fn test_font_template_is_cached() {
);
assert_eq!(
- count.get(),
+ source.find_font_count.fetch_add(0, Ordering::Relaxed),
1,
"we should only have fetched the template data from the cache thread once"
);