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(crate) 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.iter().find_map(|template| (*template).clone())
170 }
171
172 fn remove_templates_for_stylesheet(&mut self, stylesheet: &DocumentStyleSheet) {
173 let remove_if_template_matches = |template: &mut Option<FontTemplateRef>| {
174 if template
175 .as_ref()
176 .is_some_and(|template| template.borrow().stylesheet.as_ref() == Some(stylesheet))
177 {
178 *template = None;
179 }
180 };
181 remove_if_template_matches(&mut self.regular);
182 remove_if_template_matches(&mut self.bold);
183 remove_if_template_matches(&mut self.italic);
184 remove_if_template_matches(&mut self.bold_italic);
185 }
186
187 pub(crate) fn for_all_identifiers(&self, mut callback: impl FnMut(&FontIdentifier)) {
188 let mut call_if_not_none = |template: &Option<FontTemplateRef>| {
189 if let Some(template) = template {
190 callback(&template.identifier())
191 }
192 };
193 call_if_not_none(&self.regular);
194 call_if_not_none(&self.bold);
195 call_if_not_none(&self.italic);
196 call_if_not_none(&self.bold_italic);
197 }
198}
199#[derive(Clone, Debug, MallocSizeOf)]
201pub struct FontTemplates {
202 pub(crate) templates: Vec<FontTemplateRef>,
203 simple_family: Option<SimpleFamily>,
204}
205
206impl Default for FontTemplates {
207 fn default() -> Self {
208 Self {
209 templates: Default::default(),
210 simple_family: Some(SimpleFamily::default()),
211 }
212 }
213}
214
215impl FontTemplates {
216 pub fn find_for_descriptor(
218 &self,
219 descriptor_to_match: Option<&FontDescriptor>,
220 ) -> Vec<FontTemplateRef> {
221 let Some(descriptor_to_match) = descriptor_to_match else {
222 return self.templates.clone();
223 };
224
225 if self.templates.len() == 1 {
226 return vec![self.templates[0].clone()];
227 }
228
229 if let Some(template) = self
230 .simple_family
231 .as_ref()
232 .and_then(|simple_family| simple_family.find_for_descriptor(descriptor_to_match))
233 {
234 return vec![template];
235 }
236
237 let mut best_templates = Vec::new();
238 let mut best_distance = f32::MAX;
239 for template in self.templates.iter() {
240 let distance = template.descriptor_distance(descriptor_to_match);
241 if distance < best_distance {
242 best_templates = vec![template.clone()];
243 best_distance = distance
244 } else if distance == best_distance {
245 best_templates.push(template.clone());
246 }
247 }
248
249 if !best_templates.is_empty() {
250 return best_templates;
251 }
252
253 if let Some(template) = self.templates.first() {
257 return vec![template.clone()];
258 }
259
260 Vec::new()
261 }
262
263 pub fn add_template(&mut self, new_template: FontTemplate) {
264 for existing_template in &self.templates {
265 let existing_template = existing_template.borrow();
266 if *existing_template.identifier() == new_template.identifier &&
267 existing_template.descriptor == new_template.descriptor
268 {
269 return;
270 }
271 }
272
273 let new_template = FontTemplateRef::new(new_template);
274 self.templates.push(new_template.clone());
275 self.update_simple_family(new_template);
276 }
277
278 fn update_simple_family(&mut self, added_template: FontTemplateRef) {
279 let Some(simple_family) = self.simple_family.as_mut() else {
282 return;
283 };
284
285 if self.templates.len() > 4 {
286 self.simple_family = None;
287 return;
288 }
289
290 if added_template.descriptor().is_variation_font() {
292 self.simple_family = None;
293 return;
294 }
295
296 let Some(first) = self.templates.first() else {
297 warn!("Called before adding any templates.");
298 return;
299 };
300
301 let stretch = added_template.descriptor().stretch.0;
304 let style = added_template.descriptor().style.0;
305 if first.descriptor().stretch.0 != stretch || style.is_oblique() {
306 self.simple_family = None;
307 return;
308 }
309
310 let weight = added_template.descriptor().weight.0;
311 let supports_bold = weight >= FontWeight::BOLD_THRESHOLD;
312 let is_italic = style == FontStyle::ITALIC;
313 let slot = match (supports_bold, is_italic) {
314 (true, true) => &mut simple_family.bold_italic,
315 (true, false) => &mut simple_family.bold,
316 (false, true) => &mut simple_family.italic,
317 (false, false) => &mut simple_family.regular,
318 };
319
320 if slot.is_some() {
323 self.simple_family = None;
324 return;
325 }
326
327 slot.replace(added_template);
328 }
329
330 pub(crate) fn remove_templates_for_stylesheet(
331 &mut self,
332 stylesheet: &DocumentStyleSheet,
333 ) -> bool {
334 let length_before = self.templates.len();
335 self.templates
336 .retain(|template| template.borrow().stylesheet.as_ref() != Some(stylesheet));
337
338 if let Some(simple_family) = self.simple_family.as_mut() {
339 simple_family.remove_templates_for_stylesheet(stylesheet);
340 }
341
342 length_before != self.templates.len()
343 }
344
345 pub(crate) fn for_all_identifiers(&self, mut callback: impl FnMut(&FontIdentifier)) {
346 for template in self.templates.iter() {
347 callback(&template.borrow().identifier);
348 }
349 if let Some(ref simple_family) = self.simple_family {
350 simple_family.for_all_identifiers(callback)
351 }
352 }
353}