Skip to main content

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