fonts/
font_store.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::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    /// Handle a web font load finishing, adding the new font to the [`FontStore`]. If the web font
92    /// load was canceled (for instance, if the stylesheet was removed), then do nothing and return
93    /// false.
94    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        // Abort processing this web font if the originating stylesheet was removed.
101        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/// A struct that represents the available templates in a "simple family." A simple family
141/// is one that contains <= 4 available faces: regular, bold, italic, and bold italic. Having
142/// this simple family abstraction makes font matching much faster for families that don't
143/// have a complex set of fonts.
144///
145/// This optimization is taken from:
146/// <https://searchfox.org/mozilla-central/source/gfx/thebes/gfxFontEntry.cpp>.
147#[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    /// Find a font in this family that matches a given descriptor.
157    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        // This represents the preference of which font to return from the [`SimpleFamily`],
162        // given what kind of font we are requesting.
163        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/// A list of font templates that make up a given font family.
203#[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    /// Find a font in this family that matches a given descriptor.
220    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 a request is made for a font family that exists,
257        // pick the first valid font in the family if we failed
258        // to find an exact match for the descriptor.
259        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        // If this was detected to not be a simple family before, it cannot ever be one
283        // in the future.
284        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        // Variation fonts are never simple families.
294        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        // If the stretch between any of these fonts differ, it cannot be a simple family nor if this
305        // font is oblique.
306        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 the slot was already filled there are two fonts with the same simple properties
324        // and this isn't a simple family.
325        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}