1use std::borrow::ToOwned;
6use std::cell::OnceCell;
7use std::collections::HashMap;
8use std::thread;
9
10use app_units::Au;
11use base::id::RenderingGroupId;
12use compositing_traits::CrossProcessCompositorApi;
13use fonts_traits::{
14 FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, LowercaseFontFamilyName,
15 SystemFontServiceMessage, SystemFontServiceProxySender,
16};
17use ipc_channel::ipc::{self, IpcReceiver};
18use malloc_size_of::MallocSizeOf as MallocSizeOfTrait;
19use malloc_size_of_derive::MallocSizeOf;
20use profile_traits::mem::{
21 ProcessReports, ProfilerChan, Report, ReportKind, ReportsChan, perform_memory_report,
22};
23use profile_traits::path;
24use rustc_hash::FxHashMap;
25use servo_config::pref;
26use style::values::computed::font::{GenericFontFamily, SingleFontFamily};
27use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
28
29use crate::font_store::FontStore;
30use crate::platform::font_list::{
31 default_system_generic_font_family, for_each_available_family, for_each_variation,
32};
33
34#[derive(Default, MallocSizeOf)]
35struct ResolvedGenericFontFamilies {
36 default: OnceCell<LowercaseFontFamilyName>,
37 serif: OnceCell<LowercaseFontFamilyName>,
38 sans_serif: OnceCell<LowercaseFontFamilyName>,
39 monospace: OnceCell<LowercaseFontFamilyName>,
40 fantasy: OnceCell<LowercaseFontFamilyName>,
41 cursive: OnceCell<LowercaseFontFamilyName>,
42 system_ui: OnceCell<LowercaseFontFamilyName>,
43}
44
45#[derive(Eq, Hash, MallocSizeOf, PartialEq)]
46struct FontInstancesMapKey {
47 font_key: FontKey,
48 pt_size: Au,
49 variations: Vec<FontVariation>,
50 rendering_group_id: RenderingGroupId,
51 flags: FontInstanceFlags,
52}
53
54#[derive(MallocSizeOf)]
58pub struct SystemFontService {
59 port: IpcReceiver<SystemFontServiceMessage>,
60 local_families: FontStore,
61 compositor_api: CrossProcessCompositorApi,
62 webrender_fonts: HashMap<(FontIdentifier, RenderingGroupId), FontKey>,
64 font_instances: HashMap<FontInstancesMapKey, FontInstanceKey>,
65 generic_fonts: ResolvedGenericFontFamilies,
66
67 free_font_keys: FxHashMap<RenderingGroupId, Vec<FontKey>>,
73
74 free_font_instance_keys: FxHashMap<RenderingGroupId, Vec<FontInstanceKey>>,
79}
80
81impl SystemFontService {
82 pub fn spawn(
83 compositor_api: CrossProcessCompositorApi,
84 memory_profiler_sender: ProfilerChan,
85 ) -> SystemFontServiceProxySender {
86 let (sender, receiver) = ipc::channel().unwrap();
87 let memory_reporter_sender = sender.clone();
88
89 thread::Builder::new()
90 .name("SystemFontService".to_owned())
91 .spawn(move || {
92 #[allow(clippy::default_constructed_unit_structs)]
93 let mut cache = SystemFontService {
94 port: receiver,
95 local_families: Default::default(),
96 compositor_api,
97 webrender_fonts: HashMap::new(),
98 font_instances: HashMap::new(),
99 generic_fonts: Default::default(),
100 free_font_keys: Default::default(),
101 free_font_instance_keys: Default::default(),
102 };
103
104 cache.fetch_new_keys(RenderingGroupId::default());
105 cache.refresh_local_families();
106
107 memory_profiler_sender.run_with_memory_reporting(
108 || cache.run(),
109 "system-fonts".to_owned(),
110 memory_reporter_sender,
111 SystemFontServiceMessage::CollectMemoryReport,
112 );
113 })
114 .expect("Thread spawning failed");
115
116 SystemFontServiceProxySender(sender)
117 }
118
119 fn run(&mut self) {
120 loop {
121 let msg = self.port.recv().unwrap();
122
123 #[cfg(feature = "tracing")]
124 let _span =
125 tracing::trace_span!("SystemFontServiceMessage", servo_profiling = true).entered();
126 match msg {
127 SystemFontServiceMessage::GetFontTemplates(
128 font_descriptor,
129 font_family,
130 result_sender,
131 ) => {
132 let _ =
133 result_sender.send(self.get_font_templates(font_descriptor, font_family));
134 },
135 SystemFontServiceMessage::GetFontInstance(
136 rendering_group_id,
137 identifier,
138 pt_size,
139 flags,
140 variations,
141 result,
142 ) => {
143 let _ = result.send(self.get_font_instance(
144 rendering_group_id,
145 identifier,
146 pt_size,
147 flags,
148 variations,
149 ));
150 },
151 SystemFontServiceMessage::GetFontKey(rendering_group_id, result_sender) => {
152 self.fetch_new_keys(rendering_group_id);
153
154 let _ = result_sender.send(
155 self.free_font_keys
156 .get_mut(&rendering_group_id)
157 .expect("We just filled the keys")
158 .pop()
159 .unwrap(),
160 );
161 },
162 SystemFontServiceMessage::GetFontInstanceKey(rendering_group_id, result_sender) => {
163 self.fetch_new_keys(rendering_group_id);
164 let _ = result_sender.send(
165 self.free_font_instance_keys
166 .get_mut(&rendering_group_id)
167 .expect("We just filled the keys")
168 .pop()
169 .unwrap(),
170 );
171 },
172 SystemFontServiceMessage::CollectMemoryReport(report_sender) => {
173 self.collect_memory_report(report_sender);
174 },
175 SystemFontServiceMessage::Ping => (),
176 SystemFontServiceMessage::Exit(result) => {
177 let _ = result.send(());
178 break;
179 },
180 }
181 }
182 }
183
184 fn collect_memory_report(&self, report_sender: ReportsChan) {
185 perform_memory_report(|ops| {
186 let reports = vec![Report {
187 path: path!["system-fonts"],
188 kind: ReportKind::ExplicitSystemHeapSize,
189 size: self.size_of(ops),
190 }];
191 report_sender.send(ProcessReports::new(reports));
192 });
193 }
194
195 #[servo_tracing::instrument(skip_all)]
196 fn fetch_new_keys(&mut self, rendering_group_id: RenderingGroupId) {
197 if !self
198 .free_font_keys
199 .get(&rendering_group_id)
200 .is_none_or(|v| v.is_empty()) &&
201 !self
202 .free_font_instance_keys
203 .get(&rendering_group_id)
204 .is_none_or(|v| v.is_empty())
205 {
206 return;
207 }
208
209 const FREE_FONT_KEYS_BATCH_SIZE: usize = 40;
210 const FREE_FONT_INSTANCE_KEYS_BATCH_SIZE: usize = 40;
211 let (mut new_font_keys, mut new_font_instance_keys) = self.compositor_api.fetch_font_keys(
212 FREE_FONT_KEYS_BATCH_SIZE - self.free_font_keys.len(),
213 FREE_FONT_INSTANCE_KEYS_BATCH_SIZE - self.free_font_instance_keys.len(),
214 rendering_group_id,
215 );
216 self.free_font_keys
217 .entry(rendering_group_id)
218 .or_default()
219 .append(&mut new_font_keys);
220 self.free_font_instance_keys
221 .entry(rendering_group_id)
222 .or_default()
223 .append(&mut new_font_instance_keys);
224 }
225
226 #[servo_tracing::instrument(skip_all)]
227 fn get_font_templates(
228 &mut self,
229 font_descriptor: Option<FontDescriptor>,
230 font_family: SingleFontFamily,
231 ) -> Vec<FontTemplate> {
232 let templates = self.find_font_templates(font_descriptor.as_ref(), &font_family);
233 templates
234 .into_iter()
235 .map(|template| template.borrow().clone())
236 .collect()
237 }
238
239 #[servo_tracing::instrument(skip_all)]
240 fn refresh_local_families(&mut self) {
241 self.local_families.clear();
242 for_each_available_family(|family_name| {
243 self.local_families
244 .families
245 .entry(family_name.as_str().into())
246 .or_default();
247 });
248 }
249
250 #[servo_tracing::instrument(skip_all)]
251 fn find_font_templates(
252 &mut self,
253 descriptor_to_match: Option<&FontDescriptor>,
254 family: &SingleFontFamily,
255 ) -> Vec<FontTemplateRef> {
256 let family_name = self.family_name_for_single_font_family(family);
259 self.local_families
260 .families
261 .get_mut(&family_name)
262 .map(|font_templates| {
263 if font_templates.templates.is_empty() {
264 for_each_variation(&family_name, |font_template| {
265 font_templates.add_template(font_template);
266 });
267 }
268
269 font_templates.find_for_descriptor(descriptor_to_match)
270 })
271 .unwrap_or_default()
272 }
273
274 #[servo_tracing::instrument(skip_all)]
275 fn get_font_instance(
276 &mut self,
277 rendering_group_id: RenderingGroupId,
278 identifier: FontIdentifier,
279 pt_size: Au,
280 flags: FontInstanceFlags,
281 variations: Vec<FontVariation>,
282 ) -> FontInstanceKey {
283 self.fetch_new_keys(rendering_group_id);
284
285 let compositor_api = &self.compositor_api;
286 let webrender_fonts = &mut self.webrender_fonts;
287
288 let font_key = *webrender_fonts
289 .entry((identifier.clone(), rendering_group_id))
290 .or_insert_with(|| {
291 let font_key = self
292 .free_font_keys
293 .get_mut(&rendering_group_id)
294 .expect("We just filled the keys")
295 .pop()
296 .unwrap();
297 let FontIdentifier::Local(local_font_identifier) = identifier else {
298 unreachable!("Should never have a web font in the system font service");
299 };
300 compositor_api
301 .add_system_font(font_key, local_font_identifier.native_font_handle());
302 font_key
303 });
304
305 let entry_key = FontInstancesMapKey {
306 font_key,
307 pt_size,
308 variations: variations.clone(),
309 rendering_group_id,
310 flags,
311 };
312 *self.font_instances.entry(entry_key).or_insert_with(|| {
313 let font_instance_key = self
314 .free_font_instance_keys
315 .get_mut(&rendering_group_id)
316 .expect("We just filled the keys")
317 .pop()
318 .unwrap();
319 compositor_api.add_font_instance(
320 font_instance_key,
321 font_key,
322 pt_size.to_f32_px(),
323 flags,
324 variations,
325 );
326 font_instance_key
327 })
328 }
329
330 pub(crate) fn family_name_for_single_font_family(
331 &mut self,
332 family: &SingleFontFamily,
333 ) -> LowercaseFontFamilyName {
334 let generic = match family {
335 SingleFontFamily::FamilyName(family_name) => return family_name.name.clone().into(),
336 SingleFontFamily::Generic(generic) => generic,
337 };
338
339 let resolved_font = match generic {
340 GenericFontFamily::None => &self.generic_fonts.default,
341 GenericFontFamily::Serif => &self.generic_fonts.serif,
342 GenericFontFamily::SansSerif => &self.generic_fonts.sans_serif,
343 GenericFontFamily::Monospace => &self.generic_fonts.monospace,
344 GenericFontFamily::Cursive => &self.generic_fonts.cursive,
345 GenericFontFamily::Fantasy => &self.generic_fonts.fantasy,
346 GenericFontFamily::SystemUi => &self.generic_fonts.system_ui,
347 };
348
349 resolved_font
350 .get_or_init(|| {
351 let family_name = match generic {
353 GenericFontFamily::None => pref!(fonts_default),
354 GenericFontFamily::Serif => pref!(fonts_serif),
355 GenericFontFamily::SansSerif => pref!(fonts_sans_serif),
356 GenericFontFamily::Monospace => pref!(fonts_monospace),
357 _ => String::new(),
358 };
359
360 if !family_name.is_empty() {
361 return family_name.into();
362 }
363
364 default_system_generic_font_family(*generic)
366 })
367 .clone()
368 }
369}