1use std::collections::HashMap;
6use std::ops::Deref;
7use std::sync::Arc;
8
9use fonts_traits::{
10 FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, FontTemplateRefMethods,
11 IsOblique, LowercaseFontFamilyName,
12};
13use log::warn;
14use malloc_size_of_derive::MallocSizeOf;
15use parking_lot::RwLock;
16use style::stylesheets::DocumentStyleSheet;
17use style::values::computed::{FontStyle, FontWeight};
18
19#[derive(Default, MallocSizeOf)]
20pub struct FontStore {
21 pub(crate) families: HashMap<LowercaseFontFamilyName, FontTemplates>,
22 web_fonts_loading_for_stylesheets: Vec<(DocumentStyleSheet, usize)>,
23 web_fonts_loading_for_script: usize,
24}
25
26#[derive(Default, MallocSizeOf)]
27pub(crate) struct CrossThreadFontStore(#[conditional_malloc_size_of] Arc<RwLock<FontStore>>);
28
29impl Deref for CrossThreadFontStore {
30 type Target = Arc<RwLock<FontStore>>;
31 fn deref(&self) -> &Self::Target {
32 &self.0
33 }
34}
35
36impl FontStore {
37 pub(crate) fn clear(&mut self) {
38 self.families.clear();
39 }
40
41 pub(crate) fn font_load_cancelled_for_stylesheet(
42 &self,
43 stylesheet: &DocumentStyleSheet,
44 ) -> bool {
45 !self
46 .web_fonts_loading_for_stylesheets
47 .iter()
48 .any(|(loading_stylesheet, _)| loading_stylesheet == stylesheet)
49 }
50
51 pub(crate) fn handle_stylesheet_removed(&mut self, stylesheet: &DocumentStyleSheet) {
52 self.web_fonts_loading_for_stylesheets
53 .retain(|(loading_stylesheet, _)| loading_stylesheet != stylesheet);
54 }
55
56 pub(crate) fn handle_web_font_load_started_for_stylesheet(
57 &mut self,
58 stylesheet: &DocumentStyleSheet,
59 ) {
60 if let Some((_, count)) = self
61 .web_fonts_loading_for_stylesheets
62 .iter_mut()
63 .find(|(loading_stylesheet, _)| loading_stylesheet == stylesheet)
64 {
65 *count += 1;
66 } else {
67 self.web_fonts_loading_for_stylesheets
68 .push((stylesheet.clone(), 1))
69 }
70 }
71
72 fn remove_one_web_font_loading_for_stylesheet(&mut self, stylesheet: &DocumentStyleSheet) {
73 if let Some((_, count)) = self
74 .web_fonts_loading_for_stylesheets
75 .iter_mut()
76 .find(|(loading_stylesheet, _)| loading_stylesheet == stylesheet)
77 {
78 *count -= 1;
79 }
80 self.web_fonts_loading_for_stylesheets
81 .retain(|(_, count)| *count != 0);
82 }
83
84 pub(crate) fn handle_web_font_load_failed_for_stylesheet(
85 &mut self,
86 stylesheet: &DocumentStyleSheet,
87 ) {
88 self.remove_one_web_font_loading_for_stylesheet(stylesheet);
89 }
90
91 pub(crate) fn handle_web_font_loaded_for_stylesheet(
95 &mut self,
96 stylesheet: &DocumentStyleSheet,
97 family_name: LowercaseFontFamilyName,
98 new_template: FontTemplate,
99 ) -> bool {
100 if self.font_load_cancelled_for_stylesheet(stylesheet) {
102 return false;
103 }
104
105 self.add_new_template(family_name, new_template);
106
107 self.remove_one_web_font_loading_for_stylesheet(stylesheet);
108
109 true
110 }
111
112 pub(crate) fn add_new_template(
113 &mut self,
114 family_name: LowercaseFontFamilyName,
115 new_template: FontTemplate,
116 ) {
117 self.families
118 .entry(family_name)
119 .or_default()
120 .add_template(new_template);
121 }
122
123 pub(crate) fn handle_web_font_load_started_for_script(&mut self) {
124 self.web_fonts_loading_for_script += 1;
125 }
126
127 pub(crate) fn handle_web_font_load_finished_for_script(&mut self) {
128 self.web_fonts_loading_for_script -= 1;
129 }
130
131 pub(crate) fn number_of_fonts_still_loading(&self) -> usize {
132 self.web_fonts_loading_for_script +
133 self.web_fonts_loading_for_stylesheets
134 .iter()
135 .map(|(_, count)| count)
136 .sum::<usize>()
137 }
138}
139
140#[derive(Clone, Debug, Default, MallocSizeOf)]
148struct SimpleFamily {
149 regular: Option<FontTemplateRef>,
150 bold: Option<FontTemplateRef>,
151 italic: Option<FontTemplateRef>,
152 bold_italic: Option<FontTemplateRef>,
153}
154
155impl SimpleFamily {
156 fn find_for_descriptor(&self, descriptor_to_match: &FontDescriptor) -> Option<FontTemplateRef> {
158 let want_bold = descriptor_to_match.weight >= FontWeight::BOLD_THRESHOLD;
159 let want_italic = descriptor_to_match.style != FontStyle::NORMAL;
160
161 let preference = match (want_bold, want_italic) {
164 (true, true) => [&self.bold_italic, &self.italic, &self.bold, &self.regular],
165 (true, false) => [&self.bold, &self.regular, &self.bold_italic, &self.italic],
166 (false, true) => [&self.italic, &self.bold_italic, &self.regular, &self.bold],
167 (false, false) => [&self.regular, &self.bold, &self.italic, &self.bold_italic],
168 };
169 preference
170 .iter()
171 .filter_map(|template| (*template).clone())
172 .next()
173 }
174
175 fn remove_templates_for_stylesheet(&mut self, stylesheet: &DocumentStyleSheet) {
176 let remove_if_template_matches = |template: &mut Option<FontTemplateRef>| {
177 if template
178 .as_ref()
179 .is_some_and(|template| template.borrow().stylesheet.as_ref() == Some(stylesheet))
180 {
181 *template = None;
182 }
183 };
184 remove_if_template_matches(&mut self.regular);
185 remove_if_template_matches(&mut self.bold);
186 remove_if_template_matches(&mut self.italic);
187 remove_if_template_matches(&mut self.bold_italic);
188 }
189
190 pub(crate) fn for_all_identifiers(&self, mut callback: impl FnMut(&FontIdentifier)) {
191 let mut call_if_not_none = |template: &Option<FontTemplateRef>| {
192 if let Some(template) = template {
193 callback(&template.identifier())
194 }
195 };
196 call_if_not_none(&self.regular);
197 call_if_not_none(&self.bold);
198 call_if_not_none(&self.italic);
199 call_if_not_none(&self.bold_italic);
200 }
201}
202#[derive(Clone, Debug, MallocSizeOf)]
204pub struct FontTemplates {
205 pub(crate) templates: Vec<FontTemplateRef>,
206 simple_family: Option<SimpleFamily>,
207}
208
209impl Default for FontTemplates {
210 fn default() -> Self {
211 Self {
212 templates: Default::default(),
213 simple_family: Some(SimpleFamily::default()),
214 }
215 }
216}
217
218impl FontTemplates {
219 pub fn find_for_descriptor(
221 &self,
222 descriptor_to_match: Option<&FontDescriptor>,
223 ) -> Vec<FontTemplateRef> {
224 let Some(descriptor_to_match) = descriptor_to_match else {
225 return self.templates.clone();
226 };
227
228 if self.templates.len() == 1 {
229 return vec![self.templates[0].clone()];
230 }
231
232 if let Some(template) = self
233 .simple_family
234 .as_ref()
235 .and_then(|simple_family| simple_family.find_for_descriptor(descriptor_to_match))
236 {
237 return vec![template];
238 }
239
240 let mut best_templates = Vec::new();
241 let mut best_distance = f32::MAX;
242 for template in self.templates.iter() {
243 let distance = template.descriptor_distance(descriptor_to_match);
244 if distance < best_distance {
245 best_templates = vec![template.clone()];
246 best_distance = distance
247 } else if distance == best_distance {
248 best_templates.push(template.clone());
249 }
250 }
251
252 if !best_templates.is_empty() {
253 return best_templates;
254 }
255
256 if let Some(template) = self.templates.first() {
260 return vec![template.clone()];
261 }
262
263 Vec::new()
264 }
265
266 pub fn add_template(&mut self, new_template: FontTemplate) {
267 for existing_template in &self.templates {
268 let existing_template = existing_template.borrow();
269 if *existing_template.identifier() == new_template.identifier &&
270 existing_template.descriptor == new_template.descriptor
271 {
272 return;
273 }
274 }
275
276 let new_template = FontTemplateRef::new(new_template);
277 self.templates.push(new_template.clone());
278 self.update_simple_family(new_template);
279 }
280
281 fn update_simple_family(&mut self, added_template: FontTemplateRef) {
282 let Some(simple_family) = self.simple_family.as_mut() else {
285 return;
286 };
287
288 if self.templates.len() > 4 {
289 self.simple_family = None;
290 return;
291 }
292
293 if added_template.descriptor().is_variation_font() {
295 self.simple_family = None;
296 return;
297 }
298
299 let Some(first) = self.templates.first() else {
300 warn!("Called before adding any templates.");
301 return;
302 };
303
304 let stretch = added_template.descriptor().stretch.0;
307 let style = added_template.descriptor().style.0;
308 if first.descriptor().stretch.0 != stretch || style.is_oblique() {
309 self.simple_family = None;
310 return;
311 }
312
313 let weight = added_template.descriptor().weight.0;
314 let supports_bold = weight >= FontWeight::BOLD_THRESHOLD;
315 let is_italic = style == FontStyle::ITALIC;
316 let slot = match (supports_bold, is_italic) {
317 (true, true) => &mut simple_family.bold_italic,
318 (true, false) => &mut simple_family.bold,
319 (false, true) => &mut simple_family.italic,
320 (false, false) => &mut simple_family.regular,
321 };
322
323 if slot.is_some() {
326 self.simple_family = None;
327 return;
328 }
329
330 slot.replace(added_template);
331 }
332
333 pub(crate) fn remove_templates_for_stylesheet(
334 &mut self,
335 stylesheet: &DocumentStyleSheet,
336 ) -> bool {
337 let length_before = self.templates.len();
338 self.templates
339 .retain(|template| template.borrow().stylesheet.as_ref() != Some(stylesheet));
340
341 if let Some(simple_family) = self.simple_family.as_mut() {
342 simple_family.remove_templates_for_stylesheet(stylesheet);
343 }
344
345 length_before != self.templates.len()
346 }
347
348 pub(crate) fn for_all_identifiers(&self, mut callback: impl FnMut(&FontIdentifier)) {
349 for template in self.templates.iter() {
350 callback(&template.borrow().identifier);
351 }
352 if let Some(ref simple_family) = self.simple_family {
353 simple_family.for_all_identifiers(callback)
354 }
355 }
356}