fonts/
system_font_service.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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/// The system font service. There is one of these for every Servo instance. This is a thread,
55/// responsible for reading the list of system fonts, handling requests to match against
56/// them, and ensuring that only one copy of system font data is loaded at a time.
57#[derive(MallocSizeOf)]
58pub struct SystemFontService {
59    port: IpcReceiver<SystemFontServiceMessage>,
60    local_families: FontStore,
61    compositor_api: CrossProcessCompositorApi,
62    // keys already have the IdNamespace for webrender
63    webrender_fonts: HashMap<(FontIdentifier, RenderingGroupId), FontKey>,
64    font_instances: HashMap<FontInstancesMapKey, FontInstanceKey>,
65    generic_fonts: ResolvedGenericFontFamilies,
66
67    /// This is an optimization that allows the [`SystemFontService`] to send font data to
68    /// the compositor asynchronously for creating WebRender fonts, while immediately
69    /// returning a font key for that data. Once the free keys are exhausted, the
70    /// [`SystemFontService`] will fetch a new batch.
71    /// TODO: We currently do not delete the free keys if a `WebView` is removed.
72    free_font_keys: FxHashMap<RenderingGroupId, Vec<FontKey>>,
73
74    /// This is an optimization that allows the [`SystemFontService`] to create WebRender font
75    /// instances in the compositor asynchronously, while immediately returning a font
76    /// instance key for the instance. Once the free keys are exhausted, the
77    /// [`SystemFontService`] will fetch a new batch.
78    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        // TODO(Issue #188): look up localized font family names if canonical name not found
257        // look up canonical name
258        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                // First check whether the font is set in the preferences.
352                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                // Otherwise ask the platform for the default family for the generic font.
365                default_system_generic_font_family(*generic)
366            })
367            .clone()
368    }
369}