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(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    /// 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.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/// A list of font templates that make up a given font family.
200#[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    /// Find a font in this family that matches a given descriptor.
217    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 a request is made for a font family that exists,
254        // pick the first valid font in the family if we failed
255        // to find an exact match for the descriptor.
256        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        // If this was detected to not be a simple family before, it cannot ever be one
280        // in the future.
281        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        // Variation fonts are never simple families.
291        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        // If the stretch between any of these fonts differ, it cannot be a simple family nor if this
302        // font is oblique.
303        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 the slot was already filled there are two fonts with the same simple properties
321        // and this isn't a simple family.
322        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}