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