Skip to main content

fonts/
font_context.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::hash_map::Entry;
6use std::collections::{HashMap, HashSet};
7use std::default::Default;
8use std::hash::{Hash, Hasher};
9use std::iter;
10use std::sync::Arc;
11use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
12
13use app_units::Au;
14use atomic_refcell::AtomicRefCell;
15use content_security_policy::Violation;
16use fonts_traits::{
17    CSSFontFaceDescriptors, FontDescriptor, FontFaceRuleWithOrigin, FontIdentifier, FontTemplate,
18    FontTemplateRef, FontTemplateRefMethods, StylesheetWebFontLoadFinishedCallback,
19    WebFontLoadEvent, WebFontSetDifference,
20};
21use log::{debug, trace};
22use malloc_size_of::MallocSizeOf;
23use malloc_size_of_derive::MallocSizeOf;
24use net_traits::blob_url_store::UrlWithBlobClaim;
25use net_traits::policy_container::PolicyContainer;
26use net_traits::request::{
27    CredentialsMode, Destination, Referrer, RequestBuilder, RequestClient, RequestMode,
28    ServiceWorkersMode,
29};
30use net_traits::{
31    CoreResourceThread, FetchResponseMsg, ResourceFetchTiming, ResourceThreads, fetch_async,
32};
33use paint_api::CrossProcessPaintApi;
34use parking_lot::{Mutex, RwLock};
35use rustc_hash::{FxHashMap, FxHashSet};
36use servo_arc::Arc as ServoArc;
37use servo_base::id::{PainterId, WebViewId};
38use servo_config::pref;
39use servo_url::ServoUrl;
40use style::Atom;
41use style::computed_values::font_variant_caps::T as FontVariantCaps;
42use style::font_face::{
43    FontFaceSourceFormat, FontFaceSourceFormatKeyword, Source, SourceList, UrlSource,
44};
45use style::properties::generated::font_face::Descriptors as FontFaceRuleDescriptors;
46use style::properties::style_structs::Font as FontStyleStruct;
47use style::shared_lock::StylesheetGuards;
48use style::stylesheets::LockedFontFaceRule;
49use style::stylist::Stylist;
50use style::values::computed::FontVariantAlternates;
51use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFontFamily};
52use style::values::specified::font::VariantAlternates;
53use url::Url;
54use uuid::Uuid;
55use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
56
57use crate::font::{Font, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope};
58use crate::font_feature_values::{
59    AlternateKindRequiringResolution, FontFeatureValue, FontFeatureValueMap,
60    ResolvedFontVariantAlternates,
61};
62use crate::font_store::{CrossThreadFontStore, FontStore};
63use crate::platform::font::PlatformFont;
64use crate::{FontData, LowercaseFontFamilyName, PlatformFontMethods, SystemFontServiceProxy};
65
66static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
67
68#[derive(Eq, Hash, MallocSizeOf, PartialEq)]
69pub(crate) struct FontParameters {
70    pub(crate) font_key: FontKey,
71    pub(crate) pt_size: Au,
72    pub(crate) variations: Vec<FontVariation>,
73    pub(crate) flags: FontInstanceFlags,
74}
75
76pub type FontGroupRef = Arc<FontGroup>;
77
78/// The FontContext represents the per-thread/thread state necessary for
79/// working with fonts. It is the public API used by the layout and
80/// paint code. It talks directly to the system font service where
81/// required.
82#[derive(MallocSizeOf)]
83pub struct FontContext {
84    #[conditional_malloc_size_of]
85    system_font_service_proxy: Arc<SystemFontServiceProxy>,
86
87    resource_threads: Mutex<CoreResourceThread>,
88
89    /// A sender that can send messages and receive replies from `Paint`.
90    paint_api: Mutex<CrossProcessPaintApi>,
91
92    /// The actual instances of fonts ie a [`FontTemplate`] combined with a size and
93    /// other font properties, along with the font data and a platform font instance.
94    fonts: RwLock<HashMap<FontCacheKey, Option<FontRef>>>,
95
96    /// A caching map between the specification of a font in CSS style and
97    /// resolved [`FontGroup`] which contains information about all fonts that
98    /// can be selected with that style.
99    #[conditional_malloc_size_of]
100    resolved_font_groups: RwLock<HashMap<FontGroupCacheKey, FontGroupRef>>,
101
102    web_fonts: CrossThreadFontStore,
103
104    /// A collection of WebRender [`FontKey`]s generated for the web fonts that this
105    /// [`FontContext`] controls.
106    webrender_font_keys: RwLock<HashMap<FontIdentifier, FontKey>>,
107
108    /// A collection of WebRender [`FontInstanceKey`]s generated for the web fonts that
109    /// this [`FontContext`] controls.
110    webrender_font_instance_keys: RwLock<HashMap<FontParameters, FontInstanceKey>>,
111
112    /// The data for each web font [`FontIdentifier`]. This data might be used by more than one
113    /// [`FontTemplate`] as each identifier refers to a URL.
114    font_data: RwLock<HashMap<FontIdentifier, FontData>>,
115
116    have_removed_web_fonts: AtomicBool,
117
118    /// Maps from a URL to all the `@font-face` rules that are currently waiting for the load to
119    /// finish.
120    currently_downloading_fonts: Mutex<HashMap<ServoUrl, Vec<WebFontDownloadState>>>,
121
122    /// The set of `@font-face` rules that are currently present in the CSS cascade. This is not necessarily
123    /// equivalent to the rules that actually apply to the page, because rules that are invalid or not
124    /// yet downloaded are also included.
125    known_font_face_rules: Mutex<KnownFontFaceRules>,
126
127    /// A lazily-computed map of feature names from `@font-feature-value` rules.
128    font_feature_value_map: AtomicRefCell<Option<FontFeatureValueMap>>,
129
130    /// The number of fonts that are currently loading.
131    number_of_loading_web_fonts: AtomicUsize,
132}
133
134/// A callback that will be invoked on the Fetch thread if a web font download
135/// results in CSP violations. This handler will be cloned each time a new
136/// web font download is initiated.
137pub trait CspViolationHandler: Send + std::fmt::Debug + MallocSizeOf {
138    fn process_violations(&self, violations: Vec<Violation>);
139    fn clone(&self) -> Box<dyn CspViolationHandler>;
140}
141
142/// A callback that will be invoked on the Fetch thread when a web font
143/// download succeeds, providing timing information about the request.
144pub trait NetworkTimingHandler: Send + std::fmt::Debug + MallocSizeOf {
145    fn submit_timing(&self, url: ServoUrl, response: ResourceFetchTiming);
146    fn clone(&self) -> Box<dyn NetworkTimingHandler>;
147}
148
149/// Document-specific data required to fetch a web font.
150#[derive(Debug, MallocSizeOf)]
151pub struct WebFontDocumentContext {
152    pub policy_container: PolicyContainer,
153    pub request_client: RequestClient,
154    pub document_url: ServoUrl,
155    pub csp_handler: Box<dyn CspViolationHandler>,
156    pub network_timing_handler: Box<dyn NetworkTimingHandler>,
157}
158
159impl Clone for WebFontDocumentContext {
160    fn clone(&self) -> WebFontDocumentContext {
161        Self {
162            policy_container: self.policy_container.clone(),
163            request_client: self.request_client.clone(),
164            document_url: self.document_url.clone(),
165            csp_handler: self.csp_handler.clone(),
166            network_timing_handler: self.network_timing_handler.clone(),
167        }
168    }
169}
170
171impl FontContext {
172    pub fn new(
173        system_font_service_proxy: Arc<SystemFontServiceProxy>,
174        paint_api: CrossProcessPaintApi,
175        resource_threads: ResourceThreads,
176    ) -> Self {
177        Self {
178            system_font_service_proxy,
179            resource_threads: Mutex::new(resource_threads.core_thread),
180            paint_api: Mutex::new(paint_api),
181            fonts: Default::default(),
182            resolved_font_groups: Default::default(),
183            web_fonts: Default::default(),
184            webrender_font_keys: RwLock::default(),
185            webrender_font_instance_keys: RwLock::default(),
186            have_removed_web_fonts: AtomicBool::new(false),
187            font_data: RwLock::default(),
188            currently_downloading_fonts: Default::default(),
189            known_font_face_rules: Default::default(),
190            font_feature_value_map: Default::default(),
191            number_of_loading_web_fonts: Default::default(),
192        }
193    }
194
195    pub fn web_fonts_still_loading(&self) -> usize {
196        self.number_of_loading_web_fonts.load(Ordering::SeqCst)
197    }
198
199    fn get_font_data(&self, identifier: &FontIdentifier) -> Option<FontData> {
200        match identifier {
201            FontIdentifier::Web(_) | FontIdentifier::ArrayBuffer(_) => {
202                self.font_data.read().get(identifier).cloned()
203            },
204            FontIdentifier::Local(_) => None,
205        }
206    }
207
208    /// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`.
209    /// Font groups are cached, so subsequent calls with the same `style` will return a reference
210    /// to an existing `FontGroup`.
211    pub fn font_group(&self, style: ServoArc<FontStyleStruct>) -> FontGroupRef {
212        let font_size = style.font_size.computed_size().into();
213        self.font_group_with_size(style, font_size)
214    }
215
216    /// Like [`Self::font_group`], but overriding the size found in the [`FontStyleStruct`] with the given size
217    /// in pixels.
218    pub fn font_group_with_size(
219        &self,
220        style: ServoArc<FontStyleStruct>,
221        size: Au,
222    ) -> Arc<FontGroup> {
223        let cache_key = FontGroupCacheKey { size, style };
224        if let Some(font_group) = self.resolved_font_groups.read().get(&cache_key) {
225            return font_group.clone();
226        }
227
228        let mut descriptor = FontDescriptor::from(&*cache_key.style);
229        descriptor.pt_size = size;
230
231        let font_group = Arc::new(FontGroup::new(&cache_key.style, descriptor));
232        self.resolved_font_groups
233            .write()
234            .insert(cache_key, font_group.clone());
235        font_group
236    }
237
238    /// Returns a font matching the parameters. Fonts are cached, so repeated calls will return a
239    /// reference to the same underlying `Font`.
240    pub fn font(
241        &self,
242        font_template: FontTemplateRef,
243        font_descriptor: &FontDescriptor,
244    ) -> Option<FontRef> {
245        let font_descriptor = if servo_config::pref!(layout_variable_fonts_enabled) {
246            let variation_settings = font_template.borrow().compute_variations(font_descriptor);
247            &font_descriptor.with_variation_settings(variation_settings)
248        } else {
249            font_descriptor
250        };
251
252        self.get_font_maybe_synthesizing_small_caps(
253            font_template,
254            font_descriptor,
255            true, /* synthesize_small_caps */
256        )
257    }
258
259    fn get_font_maybe_synthesizing_small_caps(
260        &self,
261        font_template: FontTemplateRef,
262        font_descriptor: &FontDescriptor,
263        synthesize_small_caps: bool,
264    ) -> Option<FontRef> {
265        // TODO: (Bug #3463): Currently we only support fake small-caps
266        // painting. We should also support true small-caps (where the
267        // font supports it) in the future.
268        let synthesized_small_caps_font =
269            if font_descriptor.variant == FontVariantCaps::SmallCaps && synthesize_small_caps {
270                let mut small_caps_descriptor = font_descriptor.clone();
271                small_caps_descriptor.pt_size =
272                    font_descriptor.pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR);
273                self.get_font_maybe_synthesizing_small_caps(
274                    font_template.clone(),
275                    &small_caps_descriptor,
276                    false, /* synthesize_small_caps */
277                )
278            } else {
279                None
280            };
281
282        let cache_key = FontCacheKey {
283            font_identifier: font_template.identifier(),
284            font_descriptor: font_descriptor.clone(),
285        };
286
287        if let Some(font) = self.fonts.read().get(&cache_key).cloned() {
288            return font;
289        }
290
291        debug!(
292            "FontContext::font cache miss for font_template={:?} font_descriptor={:?}",
293            font_template, font_descriptor
294        );
295
296        // Check one more time whether the font is cached or not. There's a potential race
297        // condition, where between the time we took the read lock above and now, another thread
298        // added the font to the cache. This check makes sense, because loading a font has memory
299        // implications and is much slower than checking the map again.
300        let mut fonts = self.fonts.write();
301        if let Some(font) = fonts.get(&cache_key).cloned() {
302            return font;
303        }
304
305        // TODO: Inserting `None` into the cache here is a bit bogus. Instead we should somehow
306        // mark this template as invalid so it isn't tried again.
307        let font = self
308            .create_font(
309                font_template,
310                font_descriptor.to_owned(),
311                synthesized_small_caps_font,
312            )
313            .ok();
314        fonts.insert(cache_key, font.clone());
315        font
316    }
317
318    fn matching_web_font_templates(
319        &self,
320        descriptor_to_match: &FontDescriptor,
321        family_descriptor: &FontFamilyDescriptor,
322    ) -> Option<Vec<FontTemplateRef>> {
323        if family_descriptor.scope != FontSearchScope::Any {
324            return None;
325        }
326
327        // Do not look for generic fonts in our list of web fonts.
328        let SingleFontFamily::FamilyName(ref family_name) = family_descriptor.family else {
329            return None;
330        };
331
332        self.web_fonts
333            .read()
334            .families
335            .get(&family_name.name.clone().into())
336            .map(|templates| templates.find_for_descriptor(Some(descriptor_to_match)))
337    }
338
339    /// Try to find matching templates in this [`FontContext`], first looking in the list of web fonts and
340    /// falling back to asking the [`super::SystemFontService`] for a matching system font.
341    pub fn matching_templates(
342        &self,
343        descriptor_to_match: &FontDescriptor,
344        family_descriptor: &FontFamilyDescriptor,
345    ) -> Vec<FontTemplateRef> {
346        self.matching_web_font_templates(descriptor_to_match, family_descriptor)
347            .unwrap_or_else(|| {
348                self.system_font_service_proxy.find_matching_font_templates(
349                    Some(descriptor_to_match),
350                    &family_descriptor.family,
351                )
352            })
353    }
354
355    /// Create a `Font` for use in layout calculations, from a `FontTemplateData` returned by the
356    /// cache thread and a `FontDescriptor` which contains the styling parameters.
357    #[servo_tracing::instrument(skip_all)]
358    fn create_font(
359        &self,
360        font_template: FontTemplateRef,
361        font_descriptor: FontDescriptor,
362        synthesized_small_caps: Option<FontRef>,
363    ) -> Result<FontRef, &'static str> {
364        Ok(FontRef(Arc::new(Font::new(
365            font_template.clone(),
366            font_descriptor,
367            self.get_font_data(&font_template.identifier()),
368            synthesized_small_caps,
369        )?)))
370    }
371
372    pub(crate) fn create_font_instance_key(
373        &self,
374        font: &Font,
375        painter_id: PainterId,
376    ) -> FontInstanceKey {
377        match font.template.identifier() {
378            FontIdentifier::Local(_) => self.system_font_service_proxy.get_system_font_instance(
379                font.template.identifier(),
380                font.descriptor.pt_size,
381                font.webrender_font_instance_flags(),
382                font.variations().to_owned(),
383                painter_id,
384            ),
385            FontIdentifier::Web(_) | FontIdentifier::ArrayBuffer(_) => self
386                .create_web_font_instance(
387                    font.template.clone(),
388                    font.descriptor.pt_size,
389                    font.webrender_font_instance_flags(),
390                    font.variations().to_owned(),
391                    painter_id,
392                ),
393        }
394    }
395
396    fn create_web_font_instance(
397        &self,
398        font_template: FontTemplateRef,
399        pt_size: Au,
400        flags: FontInstanceFlags,
401        variations: Vec<FontVariation>,
402        painter_id: PainterId,
403    ) -> FontInstanceKey {
404        let identifier = font_template.identifier();
405        let font_data = self
406            .get_font_data(&identifier)
407            .expect("Web font should have associated font data");
408        let font_key = *self
409            .webrender_font_keys
410            .write()
411            .entry(identifier.clone())
412            .or_insert_with(|| {
413                let font_key = self.system_font_service_proxy.generate_font_key(painter_id);
414                self.paint_api.lock().add_font(
415                    font_key,
416                    font_data.as_ipc_shared_memory(),
417                    identifier.index(),
418                );
419                font_key
420            });
421
422        let entry_key = FontParameters {
423            font_key,
424            pt_size,
425            variations: variations.clone(),
426            flags,
427        };
428        *self
429            .webrender_font_instance_keys
430            .write()
431            .entry(entry_key)
432            .or_insert_with(|| {
433                let font_instance_key = self
434                    .system_font_service_proxy
435                    .generate_font_instance_key(painter_id);
436                self.paint_api.lock().add_font_instance(
437                    font_instance_key,
438                    font_key,
439                    pt_size.to_f32_px(),
440                    flags,
441                    variations,
442                );
443                font_instance_key
444            })
445    }
446
447    fn invalidate_font_groups_after_web_font_load(&self) {
448        self.resolved_font_groups.write().clear();
449    }
450
451    pub fn is_supported_web_font_source(source: &&Source) -> bool {
452        let url_source = match &source {
453            Source::Url(url_source) => url_source,
454            Source::Local(_) => return true,
455        };
456        let format_hint = match url_source.format_hint {
457            Some(ref format_hint) => format_hint,
458            None => return true,
459        };
460
461        if matches!(
462            format_hint,
463            FontFaceSourceFormat::Keyword(
464                FontFaceSourceFormatKeyword::Truetype |
465                    FontFaceSourceFormatKeyword::Opentype |
466                    FontFaceSourceFormatKeyword::Woff |
467                    FontFaceSourceFormatKeyword::Woff2
468            )
469        ) {
470            return true;
471        }
472
473        if let FontFaceSourceFormat::String(string) = format_hint {
474            if string == "truetype" || string == "opentype" || string == "woff" || string == "woff2"
475            {
476                return true;
477            }
478
479            return pref!(layout_variable_fonts_enabled) &&
480                (string == "truetype-variations" ||
481                    string == "opentype-variations" ||
482                    string == "woff-variations" ||
483                    string == "woff2-variations");
484        }
485
486        false
487    }
488
489    fn is_local_or_unknown_url_font(
490        &self,
491        family_name: &LowercaseFontFamilyName,
492        source: &Source,
493    ) -> bool {
494        match source {
495            Source::Url(url) => !url
496                .url
497                .url()
498                .cloned()
499                .map(ServoUrl::from)
500                .map(FontIdentifier::Web)
501                .filter(|font_identifier| self.font_data.read().contains_key(font_identifier))
502                .is_some_and(|font_identifier| {
503                    self.web_fonts
504                        .read()
505                        .families
506                        .get(family_name)
507                        .is_some_and(|templates| {
508                            templates
509                                .templates
510                                .iter()
511                                .any(|template| template.borrow().identifier == font_identifier)
512                        })
513                }),
514            Source::Local(_) => true,
515        }
516    }
517
518    /// Adds the provided new web font request to the list of pending downloads.
519    ///
520    /// Returns a boolean indicating whether a new download should be started. If there is
521    /// already a pending request for the same URL then there is no need to start a new one.
522    pub(crate) fn handle_web_font_request_started(
523        &self,
524        url: ServoUrl,
525        state: WebFontDownloadState,
526    ) -> bool {
527        let mut downloading_fonts = self.currently_downloading_fonts.lock();
528        let entry = downloading_fonts.entry(url);
529
530        // If there is no request for that URL yet then we need to start a new one.
531        let needs_new_fetch_request = matches!(entry, Entry::Vacant(_));
532
533        entry.or_default().push(state);
534
535        needs_new_fetch_request
536    }
537
538    /// Handle a web font load finishing, adding the new font to the [`FontStore`]. If the web font
539    /// load was canceled (for instance, if the stylesheet was removed), then do nothing and return
540    /// false.
541    ///
542    /// All download states waiting for this entry to load will have their promise fulfilled.
543    pub(crate) fn handle_web_font_request_succeeded(
544        &self,
545        font_data: FontData,
546        url: ServoUrl,
547    ) -> bool {
548        let Some(download_states) = self.currently_downloading_fonts.lock().remove(&url) else {
549            // No one is waiting for this web font to load ):
550            return false;
551        };
552        debug_assert!(
553            !download_states.is_empty(),
554            "Should have removed this entry"
555        );
556
557        let identifier = FontIdentifier::Web(url);
558        let Ok(handle) =
559            PlatformFont::new_from_data(identifier.clone(), &font_data, None, &[], false)
560        else {
561            return false;
562        };
563
564        self.font_data.write().insert(identifier.clone(), font_data);
565        let descriptor = handle.descriptor();
566        for download_state in download_states {
567            let mut descriptor = descriptor.clone();
568            descriptor.override_values_with_css_font_template_descriptors(
569                &download_state.css_font_face_descriptors,
570            );
571
572            let new_template = FontTemplate::new(
573                identifier.clone(),
574                descriptor,
575                download_state.initiator.font_face_rule().cloned(),
576            );
577
578            download_state.handle_web_font_load_success(new_template);
579        }
580
581        true
582    }
583
584    /// Decrement the count of font loads blocking the `document.fonts.ready` promise by one.
585    pub fn decrement_count_of_loading_fonts_by_one(&self) {
586        self.number_of_loading_web_fonts
587            .fetch_sub(1, Ordering::SeqCst);
588    }
589
590    /// Returns true iff a `@font-face` rule is part of the active set.
591    ///
592    /// A font face rule might be removed from this set if its stylesheet is removed for example.
593    pub(crate) fn is_font_face_rule_active(
594        &self,
595        target_rule: &ServoArc<LockedFontFaceRule>,
596    ) -> bool {
597        self.known_font_face_rules
598            .lock()
599            .contents
600            .values()
601            .flat_map(|bucket| bucket.iter())
602            .any(|known_rule| ServoArc::ptr_eq(&known_rule.rule_with_origin.rule, target_rule))
603    }
604}
605
606/// Tracks the progress of loading a single `@font-face` rule by trying all specified
607/// sources in order.
608#[derive(MallocSizeOf)]
609pub(crate) struct WebFontDownloadState {
610    webview_id: Option<WebViewId>,
611    css_font_face_descriptors: CSSFontFaceDescriptors,
612    remaining_sources: Vec<Source>,
613    local_fonts: FxHashMap<Atom, Option<FontTemplateRef>>,
614    #[conditional_malloc_size_of]
615    pub(crate) font_context: Arc<FontContext>,
616    initiator: WebFontLoadInitiator,
617    document_context: WebFontDocumentContext,
618}
619
620impl WebFontDownloadState {
621    fn new(
622        webview_id: Option<WebViewId>,
623        font_context: Arc<FontContext>,
624        css_font_face_descriptors: CSSFontFaceDescriptors,
625        initiator: WebFontLoadInitiator,
626        sources: Vec<Source>,
627        local_fonts: FxHashMap<Atom, Option<FontTemplateRef>>,
628        document_context: WebFontDocumentContext,
629    ) -> WebFontDownloadState {
630        WebFontDownloadState {
631            webview_id,
632            css_font_face_descriptors,
633            remaining_sources: sources,
634            local_fonts,
635            font_context,
636            initiator,
637            document_context,
638        }
639    }
640
641    pub(crate) fn handle_web_font_load_success(self, new_template: FontTemplate) {
642        let family_name = self.css_font_face_descriptors.family_name.clone();
643        match self.initiator {
644            WebFontLoadInitiator::Stylesheet(initiator) => {
645                if !self
646                    .font_context
647                    .is_font_face_rule_active(&initiator.created_by)
648                {
649                    // This font load was cancelled.
650                    if self
651                        .font_context
652                        .number_of_loading_web_fonts
653                        .fetch_sub(1, Ordering::SeqCst) ==
654                        1
655                    {
656                        // This was the last loading font - we must inform the script thread that the load
657                        // has finished because this an opportunity to resolve document.fonts.ready.
658                        (initiator.callback)(WebFontLoadEvent::UnblockedFontReadyPromise);
659                    }
660                    return;
661                }
662
663                self.font_context
664                    .web_fonts
665                    .write()
666                    .add_new_template(family_name, new_template);
667                self.font_context
668                    .invalidate_font_groups_after_web_font_load();
669
670                // Note: We intentionally do not call decrement_count_of_loading_fonts_by_one here.
671                // That is handled in the callback, which avoids document.fonts.ready being resolved
672                // prematurely.
673                (initiator.callback)(WebFontLoadEvent::LoadedSuccessfully);
674            },
675            WebFontLoadInitiator::Script(callback) => {
676                self.font_context.decrement_count_of_loading_fonts_by_one();
677                callback(family_name, Some(new_template));
678            },
679        }
680    }
681
682    /// Called when we've tried all available sources and none were usable.
683    pub(crate) fn handle_web_font_load_failure(self) {
684        let family_name = self.css_font_face_descriptors.family_name.clone();
685        match self.initiator {
686            WebFontLoadInitiator::Stylesheet(initiator) => {
687                if self
688                    .font_context
689                    .number_of_loading_web_fonts
690                    .fetch_sub(1, Ordering::SeqCst) ==
691                    1
692                {
693                    // This was the last loading font - we must inform the script thread that the load
694                    // has finished because this an opportunity to resolve document.fonts.ready.
695                    (initiator.callback)(WebFontLoadEvent::UnblockedFontReadyPromise);
696                }
697            },
698            WebFontLoadInitiator::Script(callback) => {
699                self.font_context.decrement_count_of_loading_fonts_by_one();
700                callback(family_name, None);
701            },
702        }
703    }
704}
705
706pub trait FontContextWebFontMethods {
707    fn rebuild_font_face_set(
708        &self,
709        webview_id: WebViewId,
710        stylist: &Stylist,
711        guards: &StylesheetGuards<'_>,
712        callback: StylesheetWebFontLoadFinishedCallback,
713        document_context: &WebFontDocumentContext,
714    ) -> WebFontSetDifference;
715    fn load_single_font_face_rule(
716        &self,
717        webview_id: WebViewId,
718        locked_font_face_rule: &FontFaceRuleWithOrigin,
719        guards: &StylesheetGuards<'_>,
720        callback: StylesheetWebFontLoadFinishedCallback,
721        document_context: &WebFontDocumentContext,
722    );
723    fn load_web_font_for_script(
724        &self,
725        webview_id: Option<WebViewId>,
726        sources: SourceList,
727        descriptors: CSSFontFaceDescriptors,
728        finished_callback: ScriptWebFontLoadFinishedCallback,
729        document_context: &WebFontDocumentContext,
730    );
731    fn handle_web_font_request_failed(&self, url: ServoUrl);
732}
733
734impl FontContextWebFontMethods for Arc<FontContext> {
735    fn load_single_font_face_rule(
736        &self,
737        webview_id: WebViewId,
738        locked_font_face_rule: &FontFaceRuleWithOrigin,
739        guards: &StylesheetGuards<'_>,
740        callback: StylesheetWebFontLoadFinishedCallback,
741        document_context: &WebFontDocumentContext,
742    ) {
743        let font_face_rule = locked_font_face_rule.read_with(guards);
744        let Some(ref sources) = font_face_rule.descriptors.src else {
745            return;
746        };
747
748        let css_font_face_descriptors = font_face_rule.into();
749
750        let initiator = FontFaceRuleInitiator {
751            created_by: locked_font_face_rule.rule.clone(),
752            font_face_rule: font_face_rule.descriptors.clone(),
753            callback: callback.clone(),
754        };
755
756        self.start_loading_one_web_font(
757            Some(webview_id),
758            sources,
759            css_font_face_descriptors,
760            WebFontLoadInitiator::Stylesheet(Box::new(initiator)),
761            document_context,
762        );
763    }
764    fn rebuild_font_face_set(
765        &self,
766        webview_id: WebViewId,
767        stylist: &Stylist,
768        guards: &StylesheetGuards<'_>,
769        callback: StylesheetWebFontLoadFinishedCallback,
770        document_context: &WebFontDocumentContext,
771    ) -> WebFontSetDifference {
772        let difference = self
773            .known_font_face_rules
774            .lock()
775            .diff_old_and_new_font_face_rules(stylist, guards);
776
777        for added_rule in &difference.added_font_faces {
778            self.load_single_font_face_rule(
779                webview_id,
780                added_rule,
781                guards,
782                callback.clone(),
783                document_context,
784            );
785        }
786        for removed_rule in &difference.removed_font_faces {
787            let removed_rule = removed_rule.read_with(guards);
788            self.remove_single_font_face_rule(
789                &removed_rule.descriptors,
790                &mut self.web_fonts.write(),
791            );
792        }
793
794        if !difference.removed_font_faces.is_empty() {
795            // We modified the list of available fonts, so invalidate resolved font groups.
796            self.resolved_font_groups.write().clear();
797
798            // Ensure that we clean up any WebRender resources on the next display list update.
799            self.have_removed_web_fonts.store(true, Ordering::Relaxed);
800        }
801
802        difference
803    }
804
805    fn load_web_font_for_script(
806        &self,
807        webview_id: Option<WebViewId>,
808        sources: SourceList,
809        descriptors: CSSFontFaceDescriptors,
810        finished_callback: ScriptWebFontLoadFinishedCallback,
811        document_context: &WebFontDocumentContext,
812    ) {
813        let completion_handler = WebFontLoadInitiator::Script(finished_callback);
814        self.start_loading_one_web_font(
815            webview_id,
816            &sources,
817            descriptors,
818            completion_handler,
819            document_context,
820        );
821    }
822
823    /// Called when a single URL for a `@font-face` failed to load.
824    fn handle_web_font_request_failed(&self, url: ServoUrl) {
825        let Some(subscribers) = self.currently_downloading_fonts.lock().remove(&url) else {
826            return;
827        };
828
829        for subscriber in subscribers {
830            // See if the font load was cancelled in the meantime
831            if let WebFontLoadInitiator::Stylesheet(stylesheet_initiator) = &subscriber.initiator &&
832                !self.is_font_face_rule_active(&stylesheet_initiator.created_by)
833            {
834                // This font load was cancelled.
835                if self
836                    .number_of_loading_web_fonts
837                    .fetch_sub(1, Ordering::SeqCst) ==
838                    1
839                {
840                    // This was the last loading font - we must inform the script thread that the load
841                    // has finished because this an opportunity to resolve document.fonts.ready.
842                    (stylesheet_initiator.callback)(WebFontLoadEvent::UnblockedFontReadyPromise);
843                }
844                return;
845            }
846
847            self.process_next_web_font_source(subscriber);
848        }
849    }
850}
851
852impl FontContext {
853    pub fn collect_unused_webrender_resources(
854        &self,
855        all: bool,
856    ) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
857        if all {
858            let mut webrender_font_keys = self.webrender_font_keys.write();
859            let mut webrender_font_instance_keys = self.webrender_font_instance_keys.write();
860            self.have_removed_web_fonts.store(false, Ordering::Relaxed);
861            return (
862                webrender_font_keys.drain().map(|(_, key)| key).collect(),
863                webrender_font_instance_keys
864                    .drain()
865                    .map(|(_, key)| key)
866                    .collect(),
867            );
868        }
869
870        if !self.have_removed_web_fonts.load(Ordering::Relaxed) {
871            return (Vec::new(), Vec::new());
872        }
873
874        // Lock everything to prevent adding new fonts while we are cleaning up the old ones.
875        let web_fonts = self.web_fonts.write();
876        let mut font_data = self.font_data.write();
877        let _fonts = self.fonts.write();
878        let _font_groups = self.resolved_font_groups.write();
879        let mut webrender_font_keys = self.webrender_font_keys.write();
880        let mut webrender_font_instance_keys = self.webrender_font_instance_keys.write();
881
882        let mut unused_identifiers: HashSet<FontIdentifier> =
883            webrender_font_keys.keys().cloned().collect();
884        for templates in web_fonts.families.values() {
885            templates.for_all_identifiers(|identifier| {
886                unused_identifiers.remove(identifier);
887            });
888        }
889
890        font_data.retain(|font_identifier, _| !unused_identifiers.contains(font_identifier));
891
892        self.have_removed_web_fonts.store(false, Ordering::Relaxed);
893
894        let mut removed_keys: FxHashSet<FontKey> = FxHashSet::default();
895        webrender_font_keys.retain(|identifier, font_key| {
896            if unused_identifiers.contains(identifier) {
897                removed_keys.insert(*font_key);
898                false
899            } else {
900                true
901            }
902        });
903
904        let mut removed_instance_keys: HashSet<FontInstanceKey> = HashSet::new();
905        webrender_font_instance_keys.retain(|font_param, instance_key| {
906            if removed_keys.contains(&font_param.font_key) {
907                removed_instance_keys.insert(*instance_key);
908                false
909            } else {
910                true
911            }
912        });
913
914        (
915            removed_keys.into_iter().collect(),
916            removed_instance_keys.into_iter().collect(),
917        )
918    }
919
920    /// Returns `true` if any font templates were removed.
921    fn remove_single_font_face_rule(
922        &self,
923        font_face_rule: &FontFaceRuleDescriptors,
924        font_store: &mut FontStore,
925    ) -> bool {
926        let Some(family) = font_face_rule.font_family.as_ref() else {
927            return false;
928        };
929
930        let lowercase_family_name: LowercaseFontFamilyName = family.name.clone().into();
931        let Some(known_family) = font_store.families.get_mut(&lowercase_family_name) else {
932            return false;
933        };
934        if !known_family.remove_template_for_font_face_rule(font_face_rule) {
935            return false;
936        }
937        self.fonts.write().retain(|_, font| match font {
938            Some(font) => !font
939                .template
940                .borrow()
941                .is_defined_by_font_face_rule(font_face_rule),
942            _ => true,
943        });
944
945        true
946    }
947
948    pub fn add_template_to_font_context(
949        &self,
950        family_name: LowercaseFontFamilyName,
951        new_template: FontTemplate,
952    ) {
953        self.web_fonts
954            .write()
955            .add_new_template(family_name, new_template);
956        self.invalidate_font_groups_after_web_font_load();
957    }
958
959    pub fn construct_web_font_from_data(
960        &self,
961        data: &[u8],
962        descriptors: CSSFontFaceDescriptors,
963    ) -> Option<(LowercaseFontFamilyName, FontTemplate)> {
964        let bytes = fontsan::process(data)
965            .inspect_err(|error| {
966                debug!(
967                    "Sanitiser rejected FontFace font: family={} with {error:?}",
968                    descriptors.family_name,
969                );
970            })
971            .ok()?;
972        let font_data = FontData::from_bytes(&bytes);
973
974        let identifier = FontIdentifier::ArrayBuffer(Uuid::new_v4());
975        let handle =
976            PlatformFont::new_from_data(identifier.clone(), &font_data, None, &[], false).ok()?;
977
978        let new_template = FontTemplate::new(identifier.clone(), handle.descriptor(), None);
979
980        self.font_data.write().insert(identifier, font_data);
981
982        Some((descriptors.family_name, new_template))
983    }
984
985    fn start_loading_one_web_font(
986        self: &Arc<FontContext>,
987        webview_id: Option<WebViewId>,
988        source_list: &SourceList,
989        css_font_face_descriptors: CSSFontFaceDescriptors,
990        completion_handler: WebFontLoadInitiator,
991        document_context: &WebFontDocumentContext,
992    ) {
993        self.number_of_loading_web_fonts
994            .fetch_add(1, Ordering::SeqCst);
995
996        let sources: Vec<Source> = source_list
997            .0
998            .iter()
999            .rev()
1000            .filter(Self::is_supported_web_font_source)
1001            .filter(|source| {
1002                self.is_local_or_unknown_url_font(&css_font_face_descriptors.family_name, source)
1003            })
1004            .cloned()
1005            .collect();
1006
1007        // Fetch all local fonts first, beacause if we try to fetch them later on during the process of
1008        // loading the list of web font `src`s we may be running in the context of the router thread, which
1009        // means we won't be able to seend IPC messages to the FontCacheThread.
1010        //
1011        // TODO: This is completely wrong. The specification says that `local()` font-family should match
1012        // against full PostScript names, but this is matching against font family names. This works...
1013        // sometimes.
1014        let sources_transformed = sources
1015            .iter()
1016            .filter_map(|source| {
1017                if let Source::Local(family_name) = source {
1018                    Some(family_name)
1019                } else {
1020                    None
1021                }
1022            })
1023            .map(|family_name| {
1024                let family = SingleFontFamily::FamilyName(FamilyName {
1025                    name: family_name.name.clone(),
1026                    syntax: FontFamilyNameSyntax::Quoted,
1027                });
1028                let matching_font_templates = self
1029                    .system_font_service_proxy
1030                    .find_matching_font_templates(None, &family);
1031                let value = matching_font_templates.first();
1032                (family_name.name.clone(), value.cloned())
1033            });
1034
1035        let local_fonts = FxHashMap::from_iter(sources_transformed);
1036
1037        self.process_next_web_font_source(WebFontDownloadState::new(
1038            webview_id,
1039            self.clone(),
1040            css_font_face_descriptors,
1041            completion_handler,
1042            sources,
1043            local_fonts,
1044            document_context.clone(),
1045        ));
1046    }
1047
1048    pub(crate) fn process_next_web_font_source(
1049        self: &Arc<FontContext>,
1050        mut state: WebFontDownloadState,
1051    ) {
1052        let Some(source) = state.remaining_sources.pop() else {
1053            state.handle_web_font_load_failure();
1054            return;
1055        };
1056
1057        let this = self.clone();
1058        let web_font_family_name = state.css_font_face_descriptors.family_name.clone();
1059        match source {
1060            Source::Url(url_source) => {
1061                RemoteWebFontDownloader::download(url_source, this, web_font_family_name, state)
1062            },
1063            Source::Local(ref local_family_name) => {
1064                if let Some(new_template) = state
1065                    .local_fonts
1066                    .get(&local_family_name.name)
1067                    .cloned()
1068                    .flatten()
1069                    .and_then(|local_template| {
1070                        let template = FontTemplate::new_for_local_web_font(
1071                            local_template,
1072                            &state.css_font_face_descriptors,
1073                            state.initiator.font_face_rule().cloned(),
1074                        )
1075                        .ok()?;
1076                        Some(template)
1077                    })
1078                {
1079                    state.handle_web_font_load_success(new_template);
1080                } else {
1081                    this.process_next_web_font_source(state);
1082                }
1083            },
1084        }
1085    }
1086
1087    /// Resolves the value of `font-variant-alternates` to a set of OpenType features to apply.
1088    pub fn resolve_font_variant_alternate_identifiers_for(
1089        &self,
1090        font: &FontRef,
1091        alternates: &FontVariantAlternates,
1092        stylist: &Stylist,
1093    ) -> ResolvedFontVariantAlternates {
1094        let mut resolved_alternates = ResolvedFontVariantAlternates::default();
1095        if alternates.is_empty() {
1096            return resolved_alternates;
1097        }
1098        let Some(family_name) = font.family_name() else {
1099            return resolved_alternates;
1100        };
1101
1102        for alternate in alternates.iter() {
1103            match alternate {
1104                VariantAlternates::Stylistic(stylistic) => {
1105                    let Some(FontFeatureValue::Single(value)) = self
1106                        .look_up_font_feature_alternate_name(
1107                            family_name.clone(),
1108                            AlternateKindRequiringResolution::Stylistic,
1109                            stylistic.0.clone(),
1110                            stylist,
1111                        )
1112                    else {
1113                        continue;
1114                    };
1115
1116                    resolved_alternates.stylistic = Some(value);
1117                },
1118                VariantAlternates::Styleset(styleset_list) => {
1119                    for styleset in styleset_list.iter() {
1120                        let Some(FontFeatureValue::Vector(value)) = self
1121                            .look_up_font_feature_alternate_name(
1122                                family_name.clone(),
1123                                AlternateKindRequiringResolution::Styleset,
1124                                styleset.0.clone(),
1125                                stylist,
1126                            )
1127                        else {
1128                            continue;
1129                        };
1130
1131                        resolved_alternates.styleset.extend(value.0.iter());
1132                    }
1133                },
1134                VariantAlternates::CharacterVariant(character_variant_list) => {
1135                    for character_variant in character_variant_list.iter() {
1136                        let Some(FontFeatureValue::Pair(value)) = self
1137                            .look_up_font_feature_alternate_name(
1138                                family_name.clone(),
1139                                AlternateKindRequiringResolution::CharacterVariant,
1140                                character_variant.0.clone(),
1141                                stylist,
1142                            )
1143                        else {
1144                            continue;
1145                        };
1146
1147                        resolved_alternates.character_variant.push(value);
1148                    }
1149                },
1150                VariantAlternates::Swash(swash) => {
1151                    let Some(FontFeatureValue::Single(value)) = self
1152                        .look_up_font_feature_alternate_name(
1153                            family_name.clone(),
1154                            AlternateKindRequiringResolution::Swash,
1155                            swash.0.clone(),
1156                            stylist,
1157                        )
1158                    else {
1159                        continue;
1160                    };
1161
1162                    resolved_alternates.swash = Some(value);
1163                },
1164                VariantAlternates::Ornaments(ornaments) => {
1165                    let Some(FontFeatureValue::Single(value)) = self
1166                        .look_up_font_feature_alternate_name(
1167                            family_name.clone(),
1168                            AlternateKindRequiringResolution::Ornaments,
1169                            ornaments.0.clone(),
1170                            stylist,
1171                        )
1172                    else {
1173                        continue;
1174                    };
1175
1176                    resolved_alternates.ornaments = Some(value);
1177                },
1178                VariantAlternates::Annotation(annotation) => {
1179                    let Some(FontFeatureValue::Single(value)) = self
1180                        .look_up_font_feature_alternate_name(
1181                            family_name.clone(),
1182                            AlternateKindRequiringResolution::Annotation,
1183                            annotation.0.clone(),
1184                            stylist,
1185                        )
1186                    else {
1187                        continue;
1188                    };
1189
1190                    resolved_alternates.annotation = Some(value);
1191                },
1192                VariantAlternates::HistoricalForms => {
1193                    resolved_alternates.historical_forms = true;
1194                },
1195            }
1196        }
1197
1198        resolved_alternates
1199    }
1200
1201    /// Resolves a single component of `font-variant-alternates`, like `stylistic(foobar)` to a font-specific
1202    /// set of OpenType features to apply.
1203    ///
1204    /// If the map of `@font-feature-values` rules has not yet been computed then this method
1205    /// will compute it.
1206    fn look_up_font_feature_alternate_name(
1207        &self,
1208        family_name: Atom,
1209        kind: AlternateKindRequiringResolution,
1210        name: Atom,
1211        stylist: &Stylist,
1212    ) -> Option<FontFeatureValue> {
1213        // First, check if the map was initialized previously.
1214        let read_guard = self.font_feature_value_map.borrow();
1215        if let Some(map) = &*read_guard {
1216            // This is the cheap case, we just need to read from the map
1217            map.lookup(family_name, kind, name)
1218        } else {
1219            // Map was not initialized yet - need to acquire a mutable guard and initialize it.
1220            drop(read_guard);
1221            let mut write_guard = self.font_feature_value_map.borrow_mut();
1222            if let Some(map) = &*write_guard {
1223                // We lost a race, some other thread initialized the map while we were waiting
1224                // on the lock.
1225                return map.lookup(family_name, kind, name);
1226            }
1227
1228            log::debug!("Initializing @font-feature-values map");
1229            let mut map = FontFeatureValueMap::default();
1230            stylist
1231                .iter_extra_data_origins_rev()
1232                .flat_map(|(extra_data, _)| extra_data.font_feature_values.iter())
1233                .for_each(|(rule, _)| map.add_rule(rule));
1234            let map = &*write_guard.insert(map);
1235            // Finally, perform the actual lookup
1236            map.lookup(family_name, kind, name)
1237        }
1238    }
1239
1240    pub fn invalidate_font_feature_values_map(&self) {
1241        self.font_feature_value_map.borrow_mut().take();
1242    }
1243}
1244
1245pub(crate) type ScriptWebFontLoadFinishedCallback =
1246    Box<dyn FnOnce(LowercaseFontFamilyName, Option<FontTemplate>) + Send>;
1247
1248#[derive(MallocSizeOf)]
1249pub(crate) struct FontFaceRuleInitiator {
1250    /// A reference to the `@font-face` rule that created this web font load.
1251    /// This is only used to identify the font in case it is
1252    // TODO: It is awkward that we have to carry both the locked font face rule and the
1253    // unlocked copy around. Perhaps the FontContext should have access to the shared
1254    // lock in the future.
1255    #[conditional_malloc_size_of]
1256    created_by: ServoArc<LockedFontFaceRule>,
1257    font_face_rule: FontFaceRuleDescriptors,
1258    #[ignore_malloc_size_of = "dyn Fn"]
1259    callback: StylesheetWebFontLoadFinishedCallback,
1260}
1261
1262#[derive(MallocSizeOf)]
1263pub(crate) enum WebFontLoadInitiator {
1264    Stylesheet(Box<FontFaceRuleInitiator>),
1265    Script(#[ignore_malloc_size_of = "dyn Fn"] ScriptWebFontLoadFinishedCallback),
1266}
1267
1268impl WebFontLoadInitiator {
1269    pub(crate) fn font_face_rule(&self) -> Option<&FontFaceRuleDescriptors> {
1270        match self {
1271            Self::Stylesheet(initiator) => Some(&initiator.font_face_rule),
1272            Self::Script(_) => None,
1273        }
1274    }
1275}
1276
1277struct RemoteWebFontDownloader {
1278    /// The URL of the font currently being loaded.
1279    url: ServoArc<Url>,
1280    web_font_family_name: LowercaseFontFamilyName,
1281    response_valid: bool,
1282    /// The data that has been received from the network thread so far.
1283    response_data: Vec<u8>,
1284    document_context: WebFontDocumentContext,
1285    font_context: Arc<FontContext>,
1286}
1287
1288enum DownloaderResponseResult {
1289    InProcess,
1290    Finished,
1291    Failure,
1292}
1293
1294impl RemoteWebFontDownloader {
1295    fn download(
1296        url_source: UrlSource,
1297        font_context: Arc<FontContext>,
1298        web_font_family_name: LowercaseFontFamilyName,
1299        state: WebFontDownloadState,
1300    ) {
1301        // https://drafts.csswg.org/css-fonts/#font-fetching-requirements
1302        let url = match url_source.url.url() {
1303            Some(url) => url.clone(),
1304            None => return,
1305        };
1306
1307        let webview_id = state.webview_id;
1308        let document_context = state.document_context.clone();
1309        if !font_context.handle_web_font_request_started(url.clone().into(), state) {
1310            // This URL is already being fetched for another font, and we will be
1311            // notified when that request completes.
1312            return;
1313        }
1314
1315        let request = RequestBuilder::new(
1316            webview_id,
1317            UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone().into()),
1318            Referrer::ReferrerUrl(document_context.document_url.clone()),
1319        )
1320        .destination(Destination::Font)
1321        .mode(RequestMode::CorsMode)
1322        .credentials_mode(CredentialsMode::CredentialsSameOrigin)
1323        .service_workers_mode(ServiceWorkersMode::All)
1324        .policy_container(document_context.policy_container.clone())
1325        .client(document_context.request_client.clone());
1326
1327        let core_resource_thread_clone = font_context.resource_threads.lock().clone();
1328
1329        debug!("Loading @font-face {} from {}", web_font_family_name, url);
1330        let mut downloader = Self {
1331            url,
1332            web_font_family_name,
1333            response_valid: false,
1334            response_data: Vec::new(),
1335            document_context,
1336            font_context: font_context.clone(),
1337        };
1338
1339        fetch_async(
1340            &core_resource_thread_clone,
1341            request,
1342            None,
1343            Box::new(move |response_message| {
1344                match downloader.handle_web_font_fetch_message(response_message) {
1345                    DownloaderResponseResult::InProcess => {},
1346                    DownloaderResponseResult::Finished => {
1347                        downloader.process_downloaded_font_and_signal_completion()
1348                    },
1349                    DownloaderResponseResult::Failure => {
1350                        font_context.handle_web_font_request_failed(downloader.url.clone().into());
1351                    },
1352                }
1353            }),
1354        )
1355    }
1356
1357    /// After a download finishes, try to process the downloaded data, returning true if
1358    /// the font is added successfully to the [`FontContext`] or false if it isn't.
1359    fn process_downloaded_font_and_signal_completion(&mut self) {
1360        let font_data = std::mem::take(&mut self.response_data);
1361        trace!(
1362            "Downloaded @font-face {} ({} bytes)",
1363            self.web_font_family_name,
1364            font_data.len()
1365        );
1366
1367        let font_data = match fontsan::process(&font_data) {
1368            Ok(bytes) => FontData::from_bytes(&bytes),
1369            Err(error) => {
1370                debug!(
1371                    "Sanitiser rejected web font url={:?} with {error:?}",
1372                    self.url.as_str(),
1373                );
1374                return self
1375                    .font_context
1376                    .handle_web_font_request_failed(self.url.clone().into());
1377            },
1378        };
1379
1380        let url: ServoUrl = self.url.clone().into();
1381        self.font_context
1382            .handle_web_font_request_succeeded(font_data, url);
1383    }
1384
1385    fn handle_web_font_fetch_message(
1386        &mut self,
1387        response_message: FetchResponseMsg,
1388    ) -> DownloaderResponseResult {
1389        match response_message {
1390            FetchResponseMsg::ProcessRequestBody(..) => DownloaderResponseResult::InProcess,
1391            FetchResponseMsg::ProcessCspViolations(_request_id, violations) => {
1392                self.document_context
1393                    .csp_handler
1394                    .process_violations(violations);
1395                DownloaderResponseResult::InProcess
1396            },
1397            FetchResponseMsg::ProcessResponse(_, meta_result) => {
1398                trace!(
1399                    "@font-face {} metadata ok={:?}",
1400                    self.web_font_family_name,
1401                    meta_result.is_ok()
1402                );
1403                self.response_valid = meta_result.is_ok();
1404                DownloaderResponseResult::InProcess
1405            },
1406            FetchResponseMsg::ProcessResponseChunk(_, new_bytes) => {
1407                trace!(
1408                    "@font-face {} chunk={:?}",
1409                    self.web_font_family_name, new_bytes
1410                );
1411                if self.response_valid {
1412                    self.response_data.extend(new_bytes.0)
1413                }
1414                DownloaderResponseResult::InProcess
1415            },
1416            FetchResponseMsg::ProcessResponseEOF(_, response, timing) => {
1417                trace!(
1418                    "@font-face {} EOF={:?}",
1419                    self.web_font_family_name, response
1420                );
1421                if response.is_err() || !self.response_valid {
1422                    return DownloaderResponseResult::Failure;
1423                }
1424                self.document_context
1425                    .network_timing_handler
1426                    .submit_timing(ServoUrl::from_url(self.url.as_ref().clone()), timing);
1427                DownloaderResponseResult::Finished
1428            },
1429            FetchResponseMsg::ProcessContentLength(_request_id, size) => {
1430                self.response_data.reserve(size - self.response_data.len());
1431                DownloaderResponseResult::InProcess
1432            },
1433        }
1434    }
1435}
1436
1437#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
1438struct FontCacheKey {
1439    font_identifier: FontIdentifier,
1440    font_descriptor: FontDescriptor,
1441}
1442
1443#[derive(Debug, MallocSizeOf)]
1444struct FontGroupCacheKey {
1445    #[ignore_malloc_size_of = "This is also stored as part of styling."]
1446    style: ServoArc<FontStyleStruct>,
1447    size: Au,
1448}
1449
1450impl PartialEq for FontGroupCacheKey {
1451    fn eq(&self, other: &FontGroupCacheKey) -> bool {
1452        self.style == other.style && self.size == other.size
1453    }
1454}
1455
1456impl Eq for FontGroupCacheKey {}
1457
1458impl Hash for FontGroupCacheKey {
1459    fn hash<H>(&self, hasher: &mut H)
1460    where
1461        H: Hasher,
1462    {
1463        self.style.hash.hash(hasher)
1464    }
1465}
1466
1467#[derive(Default, MallocSizeOf)]
1468struct KnownFontFaceRules {
1469    /// Used to distinguish new, incoming `@font-face` rules from existing ones.
1470    ///
1471    /// Generations alternate between true and false, which is enough to tell one generation apart from
1472    /// the next.
1473    generation: bool,
1474    /// Maps from a font family name to a list of `@font-face` rules declaring fonts
1475    /// that belong to said family.
1476    contents: HashMap<Atom, Vec<KnownFontFaceRule>>,
1477}
1478
1479#[derive(MallocSizeOf)]
1480struct KnownFontFaceRule {
1481    rule_with_origin: FontFaceRuleWithOrigin,
1482    generation: bool,
1483}
1484
1485impl KnownFontFaceRules {
1486    /// Computes the difference between the `@font-face `rules that are currently in effect
1487    /// and the ones that the `Stylist` knows about. The caller is notified about new or removed rules
1488    /// with callbacks.
1489    fn diff_old_and_new_font_face_rules(
1490        &mut self,
1491        stylist: &Stylist,
1492        guards: &StylesheetGuards<'_>,
1493    ) -> WebFontSetDifference {
1494        let mut difference = WebFontSetDifference::default();
1495        self.generation = !self.generation;
1496
1497        let font_face_rules_in_cascade_order = stylist
1498            .iter_extra_data_origins()
1499            .flat_map(|(extra_data, origin)| {
1500                extra_data.font_faces.iter().rev().zip(iter::repeat(origin))
1501            })
1502            .map(|((rule, _layer), origin)| FontFaceRuleWithOrigin::new(rule.clone(), origin));
1503
1504        // First, find any *new* font families that were not defined previously
1505        let mut number_of_unchanged_rules = 0;
1506        let number_of_previously_known_rules: usize = self
1507            .contents
1508            .values()
1509            .map(|fonts_from_family| fonts_from_family.len())
1510            .sum();
1511        for rule_with_origin in font_face_rules_in_cascade_order {
1512            let borrowed_rule = rule_with_origin.read_with(guards);
1513
1514            let Some(font_family) = borrowed_rule.descriptors.font_family.as_ref() else {
1515                // Per https://github.com/w3c/csswg-drafts/issues/1133 an @font-face rule
1516                // is valid as far as the CSS parser is concerned even if it doesn’t have
1517                // a font-family or src declaration.
1518                // However, both are required for the rule to represent an actual font face.
1519                continue;
1520            };
1521            if borrowed_rule.descriptors.src.is_none() {
1522                // @font-face rules without a src don't constitute usable font faces.
1523                continue;
1524            }
1525
1526            let known_font_faces_for_family =
1527                self.contents.entry(font_family.name.clone()).or_default();
1528
1529            let mut conflicting_declaration_with_higher_priority_exists = false;
1530            let mut index_of_existing_entry_for_this_rule = None;
1531            for (index, known_font_face) in known_font_faces_for_family.iter().enumerate() {
1532                // See if this is a entry for this @font-face that existed prior to the current update
1533                if FontFaceRuleWithOrigin::ptr_eq(
1534                    &known_font_face.rule_with_origin,
1535                    &rule_with_origin,
1536                ) {
1537                    index_of_existing_entry_for_this_rule = Some(index);
1538                }
1539
1540                // Check if there are existing declarations with higher priority that conflict
1541                if conflicting_declaration_with_higher_priority_exists {
1542                    // We already found one conflict, no need to search for more.
1543                    continue;
1544                }
1545                if known_font_face.generation != self.generation {
1546                    // This rule was not inserted yet during this update, so it was either removed or
1547                    // has lower priority than the one currently being inserted.
1548                    continue;
1549                }
1550                if font_face_rules_conflict(
1551                    &known_font_face
1552                        .rule_with_origin
1553                        .read_with(guards)
1554                        .descriptors,
1555                    &borrowed_rule.descriptors,
1556                ) {
1557                    conflicting_declaration_with_higher_priority_exists = true;
1558                }
1559            }
1560
1561            if let Some(index_of_existing_entry_for_this_rule) =
1562                index_of_existing_entry_for_this_rule
1563            {
1564                // This @font-face rule was already present in the cascade prior to this update.
1565                // But if during this update we inserted a rule with higher priority that overrides this one
1566                // then we should not update its generation so it will be dropped at the end.
1567                if conflicting_declaration_with_higher_priority_exists {
1568                    let stale_rule =
1569                        known_font_faces_for_family.remove(index_of_existing_entry_for_this_rule);
1570                    difference
1571                        .removed_font_faces
1572                        .push(stale_rule.rule_with_origin);
1573                } else {
1574                    number_of_unchanged_rules += 1;
1575                    known_font_faces_for_family[index_of_existing_entry_for_this_rule].generation =
1576                        self.generation;
1577                }
1578            } else if conflicting_declaration_with_higher_priority_exists {
1579                // This (new) rule does not apply to the document because another rule with higher cascade priority
1580                // overrides it. We can simply ignore this declaration.
1581                continue;
1582            } else {
1583                // This is a new rule that does not conflict with anything that previously existed, so insert it.
1584                difference.added_font_faces.push(rule_with_origin.clone());
1585                known_font_faces_for_family.push(KnownFontFaceRule {
1586                    rule_with_origin,
1587                    generation: self.generation,
1588                });
1589            }
1590        }
1591
1592        if number_of_unchanged_rules == number_of_previously_known_rules {
1593            // This is the common case, where the new set of known @font-face rules is a superset of
1594            // the old one after applying the cascade. In this case there is nothing more to do,
1595            // because all old @font-face rules are still present.
1596            return difference;
1597        }
1598
1599        // Remove all `@font-face` rules that were not updated - those no longer exist on the stylist.
1600        self.contents.retain(|_, known_font_faces_for_family| {
1601            known_font_faces_for_family
1602                .extract_if(.., |rule| rule.generation != self.generation)
1603                .for_each(|removed_rule| {
1604                    difference
1605                        .removed_font_faces
1606                        .push(removed_rule.rule_with_origin);
1607                });
1608
1609            !known_font_faces_for_family.is_empty()
1610        });
1611
1612        difference
1613    }
1614}
1615
1616/// Returns `true` if the two `@font-face` rules cannot both apply at the same time.
1617///
1618/// Two font faces can coexist if they are different for the purposes of font matching:
1619/// <https://drafts.csswg.org/css-fonts-4/#font-matching-algorithm>
1620///
1621/// This method does assume that the family names have already been verified to be equal.
1622fn font_face_rules_conflict(
1623    first_rule: &FontFaceRuleDescriptors,
1624    second_rule: &FontFaceRuleDescriptors,
1625) -> bool {
1626    first_rule.font_stretch == second_rule.font_stretch &&
1627        first_rule.font_style == second_rule.font_style &&
1628        first_rule.font_weight == second_rule.font_weight &&
1629        first_rule.unicode_range == second_rule.unicode_range
1630}