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