1use 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#[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 free_font_keys: Vec<FontKey>,
60
61 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 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 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 default_system_generic_font_family(*generic)
305 })
306 .clone()
307 }
308}