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::generic_channel::{self, GenericReceiver};
12use base::id::PainterId;
13use fonts_traits::{
14    FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, LowercaseFontFamilyName,
15    SystemFontServiceMessage, SystemFontServiceProxySender,
16};
17use malloc_size_of::MallocSizeOf as MallocSizeOfTrait;
18use malloc_size_of_derive::MallocSizeOf;
19use paint_api::CrossProcessPaintApi;
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    painter_id: PainterId,
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: GenericReceiver<SystemFontServiceMessage>,
60    local_families: FontStore,
61    paint_api: CrossProcessPaintApi,
62    // keys already have the IdNamespace for webrender
63    webrender_fonts: HashMap<(FontIdentifier, PainterId), 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    /// `Paint` 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<PainterId, Vec<FontKey>>,
73
74    /// This is an optimization that allows the [`SystemFontService`] to create WebRender font
75    /// instances in `Paint` 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<PainterId, Vec<FontInstanceKey>>,
79}
80
81impl SystemFontService {
82    pub fn spawn(
83        paint_api: CrossProcessPaintApi,
84        memory_profiler_sender: ProfilerChan,
85    ) -> SystemFontServiceProxySender {
86        let (sender, receiver) = generic_channel::channel().unwrap();
87        let memory_reporter_sender = sender.clone();
88
89        thread::Builder::new()
90            .name("SystemFontService".to_owned())
91            .spawn(move || {
92                let mut cache = SystemFontService {
93                    port: receiver,
94                    local_families: Default::default(),
95                    paint_api,
96                    webrender_fonts: HashMap::new(),
97                    font_instances: HashMap::new(),
98                    generic_fonts: Default::default(),
99                    free_font_keys: Default::default(),
100                    free_font_instance_keys: Default::default(),
101                };
102
103                cache.refresh_local_families();
104
105                memory_profiler_sender.run_with_memory_reporting(
106                    || cache.run(),
107                    "system-fonts".to_owned(),
108                    memory_reporter_sender,
109                    SystemFontServiceMessage::CollectMemoryReport,
110                );
111            })
112            .expect("Thread spawning failed");
113
114        SystemFontServiceProxySender(sender)
115    }
116
117    fn run(&mut self) {
118        loop {
119            let msg = self.port.recv().unwrap();
120
121            let _span = profile_traits::trace_span!("SystemFontServiceMessage").entered();
122            match msg {
123                SystemFontServiceMessage::GetFontTemplates(
124                    font_descriptor,
125                    font_family,
126                    result_sender,
127                ) => {
128                    let _ =
129                        result_sender.send(self.get_font_templates(font_descriptor, font_family));
130                },
131                SystemFontServiceMessage::GetFontInstance(
132                    painter_id,
133                    identifier,
134                    pt_size,
135                    flags,
136                    variations,
137                    result,
138                ) => {
139                    let _ = result.send(
140                        self.get_font_instance(painter_id, identifier, pt_size, flags, variations),
141                    );
142                },
143                SystemFontServiceMessage::GetFontKey(painter_id, result_sender) => {
144                    self.fetch_font_keys_if_needed(painter_id);
145
146                    let _ = result_sender.send(
147                        self.free_font_keys
148                            .get_mut(&painter_id)
149                            .expect("We just filled the keys")
150                            .pop()
151                            .unwrap(),
152                    );
153                },
154                SystemFontServiceMessage::GetFontInstanceKey(painter_id, result_sender) => {
155                    self.fetch_font_keys_if_needed(painter_id);
156                    let _ = result_sender.send(
157                        self.free_font_instance_keys
158                            .get_mut(&painter_id)
159                            .expect("We just filled the keys")
160                            .pop()
161                            .unwrap(),
162                    );
163                },
164                SystemFontServiceMessage::PrefetchFontKeys(painter_id) => {
165                    self.fetch_font_keys_if_needed(painter_id);
166                },
167                SystemFontServiceMessage::CollectMemoryReport(report_sender) => {
168                    self.collect_memory_report(report_sender);
169                },
170                SystemFontServiceMessage::Ping => (),
171                SystemFontServiceMessage::Exit(result) => {
172                    let _ = result.send(());
173                    break;
174                },
175            }
176        }
177    }
178
179    fn collect_memory_report(&self, report_sender: ReportsChan) {
180        perform_memory_report(|ops| {
181            let reports = vec![Report {
182                path: path!["system-fonts"],
183                kind: ReportKind::ExplicitSystemHeapSize,
184                size: self.size_of(ops),
185            }];
186            report_sender.send(ProcessReports::new(reports));
187        });
188    }
189
190    #[servo_tracing::instrument(skip_all)]
191    fn fetch_font_keys_if_needed(&mut self, painter_id: PainterId) {
192        let free_font_keys = self.free_font_keys.entry(painter_id).or_default();
193        let free_font_instance_keys = self.free_font_instance_keys.entry(painter_id).or_default();
194        if !free_font_keys.is_empty() && !free_font_instance_keys.is_empty() {
195            return;
196        }
197
198        const FREE_FONT_KEYS_BATCH_SIZE: usize = 40;
199        const FREE_FONT_INSTANCE_KEYS_BATCH_SIZE: usize = 40;
200        let (mut new_font_keys, mut new_font_instance_keys) = self.paint_api.fetch_font_keys(
201            FREE_FONT_KEYS_BATCH_SIZE - free_font_keys.len(),
202            FREE_FONT_INSTANCE_KEYS_BATCH_SIZE - free_font_instance_keys.len(),
203            painter_id,
204        );
205
206        free_font_keys.append(&mut new_font_keys);
207        free_font_instance_keys.append(&mut new_font_instance_keys);
208    }
209
210    #[servo_tracing::instrument(skip_all)]
211    fn get_font_templates(
212        &mut self,
213        font_descriptor: Option<FontDescriptor>,
214        font_family: SingleFontFamily,
215    ) -> Vec<FontTemplate> {
216        let templates = self.find_font_templates(font_descriptor.as_ref(), &font_family);
217        templates
218            .into_iter()
219            .map(|template| template.borrow().clone())
220            .collect()
221    }
222
223    #[servo_tracing::instrument(skip_all)]
224    fn refresh_local_families(&mut self) {
225        self.local_families.clear();
226        for_each_available_family(|family_name| {
227            self.local_families
228                .families
229                .entry(family_name.as_str().into())
230                .or_default();
231        });
232    }
233
234    #[servo_tracing::instrument(skip_all)]
235    fn find_font_templates(
236        &mut self,
237        descriptor_to_match: Option<&FontDescriptor>,
238        family: &SingleFontFamily,
239    ) -> Vec<FontTemplateRef> {
240        // TODO(Issue #188): look up localized font family names if canonical name not found
241        // look up canonical name
242        let family_name = self.family_name_for_single_font_family(family);
243        self.local_families
244            .families
245            .get_mut(&family_name)
246            .map(|font_templates| {
247                if font_templates.templates.is_empty() {
248                    for_each_variation(&family_name, |font_template| {
249                        font_templates.add_template(font_template);
250                    });
251                }
252
253                font_templates.find_for_descriptor(descriptor_to_match)
254            })
255            .unwrap_or_default()
256    }
257
258    #[servo_tracing::instrument(skip_all)]
259    fn get_font_instance(
260        &mut self,
261        painter_id: PainterId,
262        identifier: FontIdentifier,
263        pt_size: Au,
264        flags: FontInstanceFlags,
265        variations: Vec<FontVariation>,
266    ) -> FontInstanceKey {
267        self.fetch_font_keys_if_needed(painter_id);
268
269        let paint_api = &self.paint_api;
270        let webrender_fonts = &mut self.webrender_fonts;
271
272        let font_key = *webrender_fonts
273            .entry((identifier.clone(), painter_id))
274            .or_insert_with(|| {
275                let font_key = self
276                    .free_font_keys
277                    .get_mut(&painter_id)
278                    .expect("We just filled the keys")
279                    .pop()
280                    .unwrap();
281                let FontIdentifier::Local(local_font_identifier) = identifier else {
282                    unreachable!("Should never have a web font in the system font service");
283                };
284                paint_api.add_system_font(font_key, local_font_identifier.native_font_handle());
285                font_key
286            });
287
288        let entry_key = FontInstancesMapKey {
289            font_key,
290            pt_size,
291            variations: variations.clone(),
292            painter_id,
293            flags,
294        };
295        *self.font_instances.entry(entry_key).or_insert_with(|| {
296            let font_instance_key = self
297                .free_font_instance_keys
298                .get_mut(&painter_id)
299                .expect("We just filled the keys")
300                .pop()
301                .unwrap();
302            paint_api.add_font_instance(
303                font_instance_key,
304                font_key,
305                pt_size.to_f32_px(),
306                flags,
307                variations,
308            );
309            font_instance_key
310        })
311    }
312
313    pub(crate) fn family_name_for_single_font_family(
314        &mut self,
315        family: &SingleFontFamily,
316    ) -> LowercaseFontFamilyName {
317        let generic = match family {
318            SingleFontFamily::FamilyName(family_name) => return family_name.name.clone().into(),
319            SingleFontFamily::Generic(generic) => generic,
320        };
321
322        let resolved_font = match generic {
323            GenericFontFamily::None => &self.generic_fonts.default,
324            GenericFontFamily::Serif => &self.generic_fonts.serif,
325            GenericFontFamily::SansSerif => &self.generic_fonts.sans_serif,
326            GenericFontFamily::Monospace => &self.generic_fonts.monospace,
327            GenericFontFamily::Cursive => &self.generic_fonts.cursive,
328            GenericFontFamily::Fantasy => &self.generic_fonts.fantasy,
329            GenericFontFamily::SystemUi => &self.generic_fonts.system_ui,
330        };
331
332        resolved_font
333            .get_or_init(|| {
334                // First check whether the font is set in the preferences.
335                let family_name = match generic {
336                    GenericFontFamily::None => pref!(fonts_default),
337                    GenericFontFamily::Serif => pref!(fonts_serif),
338                    GenericFontFamily::SansSerif => pref!(fonts_sans_serif),
339                    GenericFontFamily::Monospace => pref!(fonts_monospace),
340                    _ => String::new(),
341                };
342
343                if !family_name.is_empty() {
344                    return family_name.into();
345                }
346
347                // Otherwise ask the platform for the default family for the generic font.
348                default_system_generic_font_family(*generic)
349            })
350            .clone()
351    }
352}