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().to_owned(),
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        let font_template_identifier = font.template.identifier();
378        match &*font_template_identifier {
379            FontIdentifier::Local(_) => self.system_font_service_proxy.get_system_font_instance(
380                font.template.identifier().to_owned(),
381                font.descriptor.pt_size,
382                font.webrender_font_instance_flags(),
383                font.variations().to_owned(),
384                painter_id,
385            ),
386            FontIdentifier::Web(_) | FontIdentifier::ArrayBuffer(_) => self
387                .create_web_font_instance(
388                    font.template.clone(),
389                    font.descriptor.pt_size,
390                    font.webrender_font_instance_flags(),
391                    font.variations().to_owned(),
392                    painter_id,
393                ),
394        }
395    }
396
397    fn create_web_font_instance(
398        &self,
399        font_template: FontTemplateRef,
400        pt_size: Au,
401        flags: FontInstanceFlags,
402        variations: Vec<FontVariation>,
403        painter_id: PainterId,
404    ) -> FontInstanceKey {
405        let identifier = font_template.identifier();
406        let font_data = self
407            .get_font_data(&identifier)
408            .expect("Web font should have associated font data");
409        let font_key = *self
410            .webrender_font_keys
411            .write()
412            .entry(identifier.clone())
413            .or_insert_with(|| {
414                let font_key = self.system_font_service_proxy.generate_font_key(painter_id);
415                self.paint_api.lock().add_font(
416                    font_key,
417                    font_data.as_ipc_shared_memory(),
418                    identifier.index(),
419                );
420                font_key
421            });
422
423        let entry_key = FontParameters {
424            font_key,
425            pt_size,
426            variations: variations.clone(),
427            flags,
428        };
429        *self
430            .webrender_font_instance_keys
431            .write()
432            .entry(entry_key)
433            .or_insert_with(|| {
434                let font_instance_key = self
435                    .system_font_service_proxy
436                    .generate_font_instance_key(painter_id);
437                self.paint_api.lock().add_font_instance(
438                    font_instance_key,
439                    font_key,
440                    pt_size.to_f32_px(),
441                    flags,
442                    variations,
443                );
444                font_instance_key
445            })
446    }
447
448    fn invalidate_font_groups_after_web_font_load(&self) {
449        self.resolved_font_groups.write().clear();
450    }
451
452    pub fn is_supported_web_font_source(source: &&Source) -> bool {
453        let url_source = match &source {
454            Source::Url(url_source) => url_source,
455            Source::Local(_) => return true,
456        };
457        let format_hint = match url_source.format_hint {
458            Some(ref format_hint) => format_hint,
459            None => return true,
460        };
461
462        if matches!(
463            format_hint,
464            FontFaceSourceFormat::Keyword(
465                FontFaceSourceFormatKeyword::Truetype |
466                    FontFaceSourceFormatKeyword::Opentype |
467                    FontFaceSourceFormatKeyword::Woff |
468                    FontFaceSourceFormatKeyword::Woff2
469            )
470        ) {
471            return true;
472        }
473
474        if let FontFaceSourceFormat::String(string) = format_hint {
475            if string == "truetype" || string == "opentype" || string == "woff" || string == "woff2"
476            {
477                return true;
478            }
479
480            return pref!(layout_variable_fonts_enabled) &&
481                (string == "truetype-variations" ||
482                    string == "opentype-variations" ||
483                    string == "woff-variations" ||
484                    string == "woff2-variations");
485        }
486
487        false
488    }
489
490    fn is_local_or_unknown_url_font(
491        &self,
492        family_name: &LowercaseFontFamilyName,
493        source: &Source,
494    ) -> bool {
495        match source {
496            Source::Url(url) => !url
497                .url
498                .url()
499                .cloned()
500                .map(ServoUrl::from)
501                .map(FontIdentifier::Web)
502                .filter(|font_identifier| self.font_data.read().contains_key(font_identifier))
503                .is_some_and(|font_identifier| {
504                    self.web_fonts
505                        .read()
506                        .families
507                        .get(family_name)
508                        .is_some_and(|templates| {
509                            templates
510                                .templates
511                                .iter()
512                                .any(|template| template.borrow().identifier == font_identifier)
513                        })
514                }),
515            Source::Local(_) => true,
516        }
517    }
518
519    /// Adds the provided new web font request to the list of pending downloads.
520    ///
521    /// Returns a boolean indicating whether a new download should be started. If there is
522    /// already a pending request for the same URL then there is no need to start a new one.
523    pub(crate) fn handle_web_font_request_started(
524        &self,
525        url: ServoUrl,
526        state: WebFontDownloadState,
527    ) -> bool {
528        let mut downloading_fonts = self.currently_downloading_fonts.lock();
529        let entry = downloading_fonts.entry(url);
530
531        // If there is no request for that URL yet then we need to start a new one.
532        let needs_new_fetch_request = matches!(entry, Entry::Vacant(_));
533
534        entry.or_default().push(state);
535
536        needs_new_fetch_request
537    }
538
539    /// Handle a web font load finishing, adding the new font to the [`FontStore`]. If the web font
540    /// load was canceled (for instance, if the stylesheet was removed), then do nothing and return
541    /// false.
542    ///
543    /// All download states waiting for this entry to load will have their promise fulfilled.
544    pub(crate) fn handle_web_font_request_succeeded(
545        &self,
546        font_data: FontData,
547        url: ServoUrl,
548    ) -> bool {
549        let Some(download_states) = self.currently_downloading_fonts.lock().remove(&url) else {
550            // No one is waiting for this web font to load ):
551            return false;
552        };
553        debug_assert!(
554            !download_states.is_empty(),
555            "Should have removed this entry"
556        );
557
558        let identifier = FontIdentifier::Web(url);
559        let Ok(handle) =
560            PlatformFont::new_from_data(identifier.clone(), &font_data, None, &[], false)
561        else {
562            return false;
563        };
564
565        self.font_data.write().insert(identifier.clone(), font_data);
566        let descriptor = handle.descriptor();
567        for download_state in download_states {
568            let mut descriptor = descriptor.clone();
569            descriptor.override_values_with_css_font_template_descriptors(
570                &download_state.css_font_face_descriptors,
571            );
572
573            let new_template = FontTemplate::new(
574                identifier.clone(),
575                descriptor,
576                download_state.initiator.font_face_rule().cloned(),
577            );
578
579            download_state.handle_web_font_load_success(new_template);
580        }
581
582        true
583    }
584
585    /// Decrement the count of font loads blocking the `document.fonts.ready` promise by one.
586    pub fn decrement_count_of_loading_fonts_by_one(&self) {
587        self.number_of_loading_web_fonts
588            .fetch_sub(1, Ordering::SeqCst);
589    }
590
591    /// Returns true iff a `@font-face` rule is part of the active set.
592    ///
593    /// A font face rule might be removed from this set if its stylesheet is removed for example.
594    pub(crate) fn is_font_face_rule_active(
595        &self,
596        target_rule: &ServoArc<LockedFontFaceRule>,
597    ) -> bool {
598        self.known_font_face_rules
599            .lock()
600            .contents
601            .values()
602            .flat_map(|bucket| bucket.iter())
603            .any(|known_rule| ServoArc::ptr_eq(&known_rule.rule_with_origin.rule, target_rule))
604    }
605}
606
607/// Tracks the progress of loading a single `@font-face` rule by trying all specified
608/// sources in order.
609#[derive(MallocSizeOf)]
610pub(crate) struct WebFontDownloadState {
611    webview_id: Option<WebViewId>,
612    css_font_face_descriptors: CSSFontFaceDescriptors,
613    remaining_sources: Vec<Source>,
614    local_fonts: FxHashMap<Atom, Option<FontTemplateRef>>,
615    #[conditional_malloc_size_of]
616    pub(crate) font_context: Arc<FontContext>,
617    initiator: WebFontLoadInitiator,
618    document_context: WebFontDocumentContext,
619}
620
621impl WebFontDownloadState {
622    fn new(
623        webview_id: Option<WebViewId>,
624        font_context: Arc<FontContext>,
625        css_font_face_descriptors: CSSFontFaceDescriptors,
626        initiator: WebFontLoadInitiator,
627        sources: Vec<Source>,
628        local_fonts: FxHashMap<Atom, Option<FontTemplateRef>>,
629        document_context: WebFontDocumentContext,
630    ) -> WebFontDownloadState {
631        WebFontDownloadState {
632            webview_id,
633            css_font_face_descriptors,
634            remaining_sources: sources,
635            local_fonts,
636            font_context,
637            initiator,
638            document_context,
639        }
640    }
641
642    pub(crate) fn handle_web_font_load_success(self, new_template: FontTemplate) {
643        let family_name = self.css_font_face_descriptors.family_name.clone();
644        match self.initiator {
645            WebFontLoadInitiator::Stylesheet(initiator) => {
646                if !self
647                    .font_context
648                    .is_font_face_rule_active(&initiator.created_by)
649                {
650                    // This font load was cancelled.
651                    if self
652                        .font_context
653                        .number_of_loading_web_fonts
654                        .fetch_sub(1, Ordering::SeqCst) ==
655                        1
656                    {
657                        // This was the last loading font - we must inform the script thread that the load
658                        // has finished because this an opportunity to resolve document.fonts.ready.
659                        (initiator.callback)(WebFontLoadEvent::UnblockedFontReadyPromise);
660                    }
661                    return;
662                }
663
664                self.font_context
665                    .web_fonts
666                    .write()
667                    .add_new_template(family_name, new_template);
668                self.font_context
669                    .invalidate_font_groups_after_web_font_load();
670
671                // Note: We intentionally do not call decrement_count_of_loading_fonts_by_one here.
672                // That is handled in the callback, which avoids document.fonts.ready being resolved
673                // prematurely.
674                (initiator.callback)(WebFontLoadEvent::LoadedSuccessfully);
675            },
676            WebFontLoadInitiator::Script(callback) => {
677                self.font_context.decrement_count_of_loading_fonts_by_one();
678                callback(family_name, Some(new_template));
679            },
680        }
681    }
682
683    /// Called when we've tried all available sources and none were usable.
684    pub(crate) fn handle_web_font_load_failure(self) {
685        let family_name = self.css_font_face_descriptors.family_name.clone();
686        match self.initiator {
687            WebFontLoadInitiator::Stylesheet(initiator) => {
688                if self
689                    .font_context
690                    .number_of_loading_web_fonts
691                    .fetch_sub(1, Ordering::SeqCst) ==
692                    1
693                {
694                    // This was the last loading font - we must inform the script thread that the load
695                    // has finished because this an opportunity to resolve document.fonts.ready.
696                    (initiator.callback)(WebFontLoadEvent::UnblockedFontReadyPromise);
697                }
698            },
699            WebFontLoadInitiator::Script(callback) => {
700                self.font_context.decrement_count_of_loading_fonts_by_one();
701                callback(family_name, None);
702            },
703        }
704    }
705}
706
707pub trait FontContextWebFontMethods {
708    fn rebuild_font_face_set(
709        &self,
710        webview_id: WebViewId,
711        stylist: &Stylist,
712        guards: &StylesheetGuards<'_>,
713        callback: StylesheetWebFontLoadFinishedCallback,
714        document_context: &WebFontDocumentContext,
715    ) -> WebFontSetDifference;
716    fn load_single_font_face_rule(
717        &self,
718        webview_id: WebViewId,
719        locked_font_face_rule: &FontFaceRuleWithOrigin,
720        guards: &StylesheetGuards<'_>,
721        callback: StylesheetWebFontLoadFinishedCallback,
722        document_context: &WebFontDocumentContext,
723    );
724    fn load_web_font_for_script(
725        &self,
726        webview_id: Option<WebViewId>,
727        sources: SourceList,
728        descriptors: CSSFontFaceDescriptors,
729        finished_callback: ScriptWebFontLoadFinishedCallback,
730        document_context: &WebFontDocumentContext,
731    );
732    fn handle_web_font_request_failed(&self, url: ServoUrl);
733}
734
735impl FontContextWebFontMethods for Arc<FontContext> {
736    fn load_single_font_face_rule(
737        &self,
738        webview_id: WebViewId,
739        locked_font_face_rule: &FontFaceRuleWithOrigin,
740        guards: &StylesheetGuards<'_>,
741        callback: StylesheetWebFontLoadFinishedCallback,
742        document_context: &WebFontDocumentContext,
743    ) {
744        let font_face_rule = locked_font_face_rule.read_with(guards);
745        let Some(ref sources) = font_face_rule.descriptors.src else {
746            return;
747        };
748
749        let css_font_face_descriptors = font_face_rule.into();
750
751        let initiator = FontFaceRuleInitiator {
752            created_by: locked_font_face_rule.rule.clone(),
753            font_face_rule: font_face_rule.descriptors.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,
782                guards,
783                callback.clone(),
784                document_context,
785            );
786        }
787        for removed_rule in &difference.removed_font_faces {
788            let removed_rule = removed_rule.read_with(guards);
789            self.remove_single_font_face_rule(
790                &removed_rule.descriptors,
791                &mut self.web_fonts.write(),
792            );
793        }
794
795        if !difference.removed_font_faces.is_empty() {
796            // We modified the list of available fonts, so invalidate resolved font groups.
797            self.resolved_font_groups.write().clear();
798
799            // Ensure that we clean up any WebRender resources on the next display list update.
800            self.have_removed_web_fonts.store(true, Ordering::Relaxed);
801        }
802
803        difference
804    }
805
806    fn load_web_font_for_script(
807        &self,
808        webview_id: Option<WebViewId>,
809        sources: SourceList,
810        descriptors: CSSFontFaceDescriptors,
811        finished_callback: ScriptWebFontLoadFinishedCallback,
812        document_context: &WebFontDocumentContext,
813    ) {
814        let completion_handler = WebFontLoadInitiator::Script(finished_callback);
815        self.start_loading_one_web_font(
816            webview_id,
817            &sources,
818            descriptors,
819            completion_handler,
820            document_context,
821        );
822    }
823
824    /// Called when a single URL for a `@font-face` failed to load.
825    fn handle_web_font_request_failed(&self, url: ServoUrl) {
826        let Some(subscribers) = self.currently_downloading_fonts.lock().remove(&url) else {
827            return;
828        };
829
830        for subscriber in subscribers {
831            // See if the font load was cancelled in the meantime
832            if let WebFontLoadInitiator::Stylesheet(stylesheet_initiator) = &subscriber.initiator &&
833                !self.is_font_face_rule_active(&stylesheet_initiator.created_by)
834            {
835                // This font load was cancelled.
836                if self
837                    .number_of_loading_web_fonts
838                    .fetch_sub(1, Ordering::SeqCst) ==
839                    1
840                {
841                    // This was the last loading font - we must inform the script thread that the load
842                    // has finished because this an opportunity to resolve document.fonts.ready.
843                    (stylesheet_initiator.callback)(WebFontLoadEvent::UnblockedFontReadyPromise);
844                }
845                return;
846            }
847
848            self.process_next_web_font_source(subscriber);
849        }
850    }
851}
852
853impl FontContext {
854    pub fn collect_unused_webrender_resources(
855        &self,
856        all: bool,
857    ) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
858        if all {
859            let mut webrender_font_keys = self.webrender_font_keys.write();
860            let mut webrender_font_instance_keys = self.webrender_font_instance_keys.write();
861            self.have_removed_web_fonts.store(false, Ordering::Relaxed);
862            return (
863                webrender_font_keys.drain().map(|(_, key)| key).collect(),
864                webrender_font_instance_keys
865                    .drain()
866                    .map(|(_, key)| key)
867                    .collect(),
868            );
869        }
870
871        if !self.have_removed_web_fonts.load(Ordering::Relaxed) {
872            return (Vec::new(), Vec::new());
873        }
874
875        // Lock everything to prevent adding new fonts while we are cleaning up the old ones.
876        let web_fonts = self.web_fonts.write();
877        let mut font_data = self.font_data.write();
878        let _fonts = self.fonts.write();
879        let _font_groups = self.resolved_font_groups.write();
880        let mut webrender_font_keys = self.webrender_font_keys.write();
881        let mut webrender_font_instance_keys = self.webrender_font_instance_keys.write();
882
883        let mut unused_identifiers: HashSet<FontIdentifier> =
884            webrender_font_keys.keys().cloned().collect();
885        for templates in web_fonts.families.values() {
886            templates.for_all_identifiers(|identifier| {
887                unused_identifiers.remove(identifier);
888            });
889        }
890
891        font_data.retain(|font_identifier, _| !unused_identifiers.contains(font_identifier));
892
893        self.have_removed_web_fonts.store(false, Ordering::Relaxed);
894
895        let mut removed_keys: FxHashSet<FontKey> = FxHashSet::default();
896        webrender_font_keys.retain(|identifier, font_key| {
897            if unused_identifiers.contains(identifier) {
898                removed_keys.insert(*font_key);
899                false
900            } else {
901                true
902            }
903        });
904
905        let mut removed_instance_keys: HashSet<FontInstanceKey> = HashSet::new();
906        webrender_font_instance_keys.retain(|font_param, instance_key| {
907            if removed_keys.contains(&font_param.font_key) {
908                removed_instance_keys.insert(*instance_key);
909                false
910            } else {
911                true
912            }
913        });
914
915        (
916            removed_keys.into_iter().collect(),
917            removed_instance_keys.into_iter().collect(),
918        )
919    }
920
921    /// Returns `true` if any font templates were removed.
922    fn remove_single_font_face_rule(
923        &self,
924        font_face_rule: &FontFaceRuleDescriptors,
925        font_store: &mut FontStore,
926    ) -> bool {
927        let Some(family) = font_face_rule.font_family.as_ref() else {
928            return false;
929        };
930
931        let lowercase_family_name: LowercaseFontFamilyName = family.name.clone().into();
932        let Some(known_family) = font_store.families.get_mut(&lowercase_family_name) else {
933            return false;
934        };
935        if !known_family.remove_template_for_font_face_rule(font_face_rule) {
936            return false;
937        }
938        self.fonts.write().retain(|_, font| match font {
939            Some(font) => !font
940                .template
941                .borrow()
942                .is_defined_by_font_face_rule(font_face_rule),
943            _ => true,
944        });
945
946        true
947    }
948
949    pub fn add_template_to_font_context(
950        &self,
951        family_name: LowercaseFontFamilyName,
952        new_template: FontTemplate,
953    ) {
954        self.web_fonts
955            .write()
956            .add_new_template(family_name, new_template);
957        self.invalidate_font_groups_after_web_font_load();
958    }
959
960    pub fn construct_web_font_from_data(
961        &self,
962        data: &[u8],
963        descriptors: CSSFontFaceDescriptors,
964    ) -> Option<(LowercaseFontFamilyName, FontTemplate)> {
965        let bytes = fontsan::process(data)
966            .inspect_err(|error| {
967                debug!(
968                    "Sanitiser rejected FontFace font: family={} with {error:?}",
969                    descriptors.family_name,
970                );
971            })
972            .ok()?;
973        let font_data = FontData::from_bytes(&bytes);
974
975        let identifier = FontIdentifier::ArrayBuffer(Uuid::new_v4());
976        let handle =
977            PlatformFont::new_from_data(identifier.clone(), &font_data, None, &[], false).ok()?;
978
979        let new_template = FontTemplate::new(identifier.clone(), handle.descriptor(), None);
980
981        self.font_data.write().insert(identifier, font_data);
982
983        Some((descriptors.family_name, new_template))
984    }
985
986    fn start_loading_one_web_font(
987        self: &Arc<FontContext>,
988        webview_id: Option<WebViewId>,
989        source_list: &SourceList,
990        css_font_face_descriptors: CSSFontFaceDescriptors,
991        completion_handler: WebFontLoadInitiator,
992        document_context: &WebFontDocumentContext,
993    ) {
994        self.number_of_loading_web_fonts
995            .fetch_add(1, Ordering::SeqCst);
996
997        let sources: Vec<Source> = source_list
998            .0
999            .iter()
1000            .rev()
1001            .filter(Self::is_supported_web_font_source)
1002            .filter(|source| {
1003                self.is_local_or_unknown_url_font(&css_font_face_descriptors.family_name, source)
1004            })
1005            .cloned()
1006            .collect();
1007
1008        // Fetch all local fonts first, beacause if we try to fetch them later on during the process of
1009        // loading the list of web font `src`s we may be running in the context of the router thread, which
1010        // means we won't be able to seend IPC messages to the FontCacheThread.
1011        //
1012        // TODO: This is completely wrong. The specification says that `local()` font-family should match
1013        // against full PostScript names, but this is matching against font family names. This works...
1014        // sometimes.
1015        let sources_transformed = sources
1016            .iter()
1017            .filter_map(|source| {
1018                if let Source::Local(family_name) = source {
1019                    Some(family_name)
1020                } else {
1021                    None
1022                }
1023            })
1024            .map(|family_name| {
1025                let family = SingleFontFamily::FamilyName(FamilyName {
1026                    name: family_name.name.clone(),
1027                    syntax: FontFamilyNameSyntax::Quoted,
1028                });
1029                let matching_font_templates = self
1030                    .system_font_service_proxy
1031                    .find_matching_font_templates(None, &family);
1032                let value = matching_font_templates.first();
1033                (family_name.name.clone(), value.cloned())
1034            });
1035
1036        let local_fonts = FxHashMap::from_iter(sources_transformed);
1037
1038        self.process_next_web_font_source(WebFontDownloadState::new(
1039            webview_id,
1040            self.clone(),
1041            css_font_face_descriptors,
1042            completion_handler,
1043            sources,
1044            local_fonts,
1045            document_context.clone(),
1046        ));
1047    }
1048
1049    pub(crate) fn process_next_web_font_source(
1050        self: &Arc<FontContext>,
1051        mut state: WebFontDownloadState,
1052    ) {
1053        let Some(source) = state.remaining_sources.pop() else {
1054            state.handle_web_font_load_failure();
1055            return;
1056        };
1057
1058        let this = self.clone();
1059        let web_font_family_name = state.css_font_face_descriptors.family_name.clone();
1060        match source {
1061            Source::Url(url_source) => {
1062                RemoteWebFontDownloader::download(url_source, this, web_font_family_name, state)
1063            },
1064            Source::Local(ref local_family_name) => {
1065                if let Some(new_template) = state
1066                    .local_fonts
1067                    .get(&local_family_name.name)
1068                    .cloned()
1069                    .flatten()
1070                    .and_then(|local_template| {
1071                        let template = FontTemplate::new_for_local_web_font(
1072                            local_template,
1073                            &state.css_font_face_descriptors,
1074                            state.initiator.font_face_rule().cloned(),
1075                        )
1076                        .ok()?;
1077                        Some(template)
1078                    })
1079                {
1080                    state.handle_web_font_load_success(new_template);
1081                } else {
1082                    this.process_next_web_font_source(state);
1083                }
1084            },
1085        }
1086    }
1087
1088    /// Resolves the value of `font-variant-alternates` to a set of OpenType features to apply.
1089    pub fn resolve_font_variant_alternate_identifiers_for(
1090        &self,
1091        font: &FontRef,
1092        alternates: &FontVariantAlternates,
1093        stylist: &Stylist,
1094    ) -> ResolvedFontVariantAlternates {
1095        let mut resolved_alternates = ResolvedFontVariantAlternates::default();
1096        if alternates.is_empty() {
1097            return resolved_alternates;
1098        }
1099        let Some(family_name) = font.family_name() else {
1100            return resolved_alternates;
1101        };
1102
1103        for alternate in alternates.iter() {
1104            match alternate {
1105                VariantAlternates::Stylistic(stylistic) => {
1106                    let Some(FontFeatureValue::Single(value)) = self
1107                        .look_up_font_feature_alternate_name(
1108                            family_name.clone(),
1109                            AlternateKindRequiringResolution::Stylistic,
1110                            stylistic.0.clone(),
1111                            stylist,
1112                        )
1113                    else {
1114                        continue;
1115                    };
1116
1117                    resolved_alternates.stylistic = Some(value);
1118                },
1119                VariantAlternates::Styleset(styleset_list) => {
1120                    for styleset in styleset_list.iter() {
1121                        let Some(FontFeatureValue::Vector(value)) = self
1122                            .look_up_font_feature_alternate_name(
1123                                family_name.clone(),
1124                                AlternateKindRequiringResolution::Styleset,
1125                                styleset.0.clone(),
1126                                stylist,
1127                            )
1128                        else {
1129                            continue;
1130                        };
1131
1132                        resolved_alternates.styleset.extend(value.0.iter());
1133                    }
1134                },
1135                VariantAlternates::CharacterVariant(character_variant_list) => {
1136                    for character_variant in character_variant_list.iter() {
1137                        let Some(FontFeatureValue::Pair(value)) = self
1138                            .look_up_font_feature_alternate_name(
1139                                family_name.clone(),
1140                                AlternateKindRequiringResolution::CharacterVariant,
1141                                character_variant.0.clone(),
1142                                stylist,
1143                            )
1144                        else {
1145                            continue;
1146                        };
1147
1148                        resolved_alternates.character_variant.push(value);
1149                    }
1150                },
1151                VariantAlternates::Swash(swash) => {
1152                    let Some(FontFeatureValue::Single(value)) = self
1153                        .look_up_font_feature_alternate_name(
1154                            family_name.clone(),
1155                            AlternateKindRequiringResolution::Swash,
1156                            swash.0.clone(),
1157                            stylist,
1158                        )
1159                    else {
1160                        continue;
1161                    };
1162
1163                    resolved_alternates.swash = Some(value);
1164                },
1165                VariantAlternates::Ornaments(ornaments) => {
1166                    let Some(FontFeatureValue::Single(value)) = self
1167                        .look_up_font_feature_alternate_name(
1168                            family_name.clone(),
1169                            AlternateKindRequiringResolution::Ornaments,
1170                            ornaments.0.clone(),
1171                            stylist,
1172                        )
1173                    else {
1174                        continue;
1175                    };
1176
1177                    resolved_alternates.ornaments = Some(value);
1178                },
1179                VariantAlternates::Annotation(annotation) => {
1180                    let Some(FontFeatureValue::Single(value)) = self
1181                        .look_up_font_feature_alternate_name(
1182                            family_name.clone(),
1183                            AlternateKindRequiringResolution::Annotation,
1184                            annotation.0.clone(),
1185                            stylist,
1186                        )
1187                    else {
1188                        continue;
1189                    };
1190
1191                    resolved_alternates.annotation = Some(value);
1192                },
1193                VariantAlternates::HistoricalForms => {
1194                    resolved_alternates.historical_forms = true;
1195                },
1196            }
1197        }
1198
1199        resolved_alternates
1200    }
1201
1202    /// Resolves a single component of `font-variant-alternates`, like `stylistic(foobar)` to a font-specific
1203    /// set of OpenType features to apply.
1204    ///
1205    /// If the map of `@font-feature-values` rules has not yet been computed then this method
1206    /// will compute it.
1207    fn look_up_font_feature_alternate_name(
1208        &self,
1209        family_name: Atom,
1210        kind: AlternateKindRequiringResolution,
1211        name: Atom,
1212        stylist: &Stylist,
1213    ) -> Option<FontFeatureValue> {
1214        // First, check if the map was initialized previously.
1215        let read_guard = self.font_feature_value_map.borrow();
1216        if let Some(map) = &*read_guard {
1217            // This is the cheap case, we just need to read from the map
1218            map.lookup(family_name, kind, name)
1219        } else {
1220            // Map was not initialized yet - need to acquire a mutable guard and initialize it.
1221            drop(read_guard);
1222            let mut write_guard = self.font_feature_value_map.borrow_mut();
1223            if let Some(map) = &*write_guard {
1224                // We lost a race, some other thread initialized the map while we were waiting
1225                // on the lock.
1226                return map.lookup(family_name, kind, name);
1227            }
1228
1229            log::debug!("Initializing @font-feature-values map");
1230            let mut map = FontFeatureValueMap::default();
1231            stylist
1232                .iter_extra_data_origins_rev()
1233                .flat_map(|(extra_data, _)| extra_data.font_feature_values.iter())
1234                .for_each(|(rule, _)| map.add_rule(rule));
1235            let map = &*write_guard.insert(map);
1236            // Finally, perform the actual lookup
1237            map.lookup(family_name, kind, name)
1238        }
1239    }
1240
1241    pub fn invalidate_font_feature_values_map(&self) {
1242        self.font_feature_value_map.borrow_mut().take();
1243    }
1244}
1245
1246pub(crate) type ScriptWebFontLoadFinishedCallback =
1247    Box<dyn FnOnce(LowercaseFontFamilyName, Option<FontTemplate>) + Send>;
1248
1249#[derive(MallocSizeOf)]
1250pub(crate) struct FontFaceRuleInitiator {
1251    /// A reference to the `@font-face` rule that created this web font load.
1252    /// This is only used to identify the font in case it is
1253    // TODO: It is awkward that we have to carry both the locked font face rule and the
1254    // unlocked copy around. Perhaps the FontContext should have access to the shared
1255    // lock in the future.
1256    #[conditional_malloc_size_of]
1257    created_by: ServoArc<LockedFontFaceRule>,
1258    font_face_rule: FontFaceRuleDescriptors,
1259    #[ignore_malloc_size_of = "dyn Fn"]
1260    callback: StylesheetWebFontLoadFinishedCallback,
1261}
1262
1263#[derive(MallocSizeOf)]
1264pub(crate) enum WebFontLoadInitiator {
1265    Stylesheet(Box<FontFaceRuleInitiator>),
1266    Script(#[ignore_malloc_size_of = "dyn Fn"] ScriptWebFontLoadFinishedCallback),
1267}
1268
1269impl WebFontLoadInitiator {
1270    pub(crate) fn font_face_rule(&self) -> Option<&FontFaceRuleDescriptors> {
1271        match self {
1272            Self::Stylesheet(initiator) => Some(&initiator.font_face_rule),
1273            Self::Script(_) => None,
1274        }
1275    }
1276}
1277
1278struct RemoteWebFontDownloader {
1279    /// The URL of the font currently being loaded.
1280    url: ServoArc<Url>,
1281    web_font_family_name: LowercaseFontFamilyName,
1282    response_valid: bool,
1283    /// The data that has been received from the network thread so far.
1284    response_data: Vec<u8>,
1285    document_context: WebFontDocumentContext,
1286    font_context: Arc<FontContext>,
1287}
1288
1289enum DownloaderResponseResult {
1290    InProcess,
1291    Finished,
1292    Failure,
1293}
1294
1295impl RemoteWebFontDownloader {
1296    fn download(
1297        url_source: UrlSource,
1298        font_context: Arc<FontContext>,
1299        web_font_family_name: LowercaseFontFamilyName,
1300        state: WebFontDownloadState,
1301    ) {
1302        // https://drafts.csswg.org/css-fonts/#font-fetching-requirements
1303        let url = match url_source.url.url() {
1304            Some(url) => url.clone(),
1305            None => return,
1306        };
1307
1308        let webview_id = state.webview_id;
1309        let document_context = state.document_context.clone();
1310        if !font_context.handle_web_font_request_started(url.clone().into(), state) {
1311            // This URL is already being fetched for another font, and we will be
1312            // notified when that request completes.
1313            return;
1314        }
1315
1316        let request = RequestBuilder::new(
1317            webview_id,
1318            UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone().into()),
1319            Referrer::ReferrerUrl(document_context.document_url.clone()),
1320        )
1321        .destination(Destination::Font)
1322        .mode(RequestMode::CorsMode)
1323        .credentials_mode(CredentialsMode::CredentialsSameOrigin)
1324        .service_workers_mode(ServiceWorkersMode::All)
1325        .policy_container(document_context.policy_container.clone())
1326        .client(document_context.request_client.clone());
1327
1328        let core_resource_thread_clone = font_context.resource_threads.lock().clone();
1329
1330        debug!("Loading @font-face {} from {}", web_font_family_name, url);
1331        let mut downloader = Self {
1332            url,
1333            web_font_family_name,
1334            response_valid: false,
1335            response_data: Vec::new(),
1336            document_context,
1337            font_context: font_context.clone(),
1338        };
1339
1340        fetch_async(
1341            &core_resource_thread_clone,
1342            request,
1343            None,
1344            Box::new(move |response_message| {
1345                match downloader.handle_web_font_fetch_message(response_message) {
1346                    DownloaderResponseResult::InProcess => {},
1347                    DownloaderResponseResult::Finished => {
1348                        downloader.process_downloaded_font_and_signal_completion()
1349                    },
1350                    DownloaderResponseResult::Failure => {
1351                        font_context.handle_web_font_request_failed(downloader.url.clone().into());
1352                    },
1353                }
1354            }),
1355        )
1356    }
1357
1358    /// After a download finishes, try to process the downloaded data, returning true if
1359    /// the font is added successfully to the [`FontContext`] or false if it isn't.
1360    fn process_downloaded_font_and_signal_completion(&mut self) {
1361        let font_data = std::mem::take(&mut self.response_data);
1362        trace!(
1363            "Downloaded @font-face {} ({} bytes)",
1364            self.web_font_family_name,
1365            font_data.len()
1366        );
1367
1368        let font_data = match fontsan::process(&font_data) {
1369            Ok(bytes) => FontData::from_bytes(&bytes),
1370            Err(error) => {
1371                debug!(
1372                    "Sanitiser rejected web font url={:?} with {error:?}",
1373                    self.url.as_str(),
1374                );
1375                return self
1376                    .font_context
1377                    .handle_web_font_request_failed(self.url.clone().into());
1378            },
1379        };
1380
1381        let url: ServoUrl = self.url.clone().into();
1382        self.font_context
1383            .handle_web_font_request_succeeded(font_data, url);
1384    }
1385
1386    fn handle_web_font_fetch_message(
1387        &mut self,
1388        response_message: FetchResponseMsg,
1389    ) -> DownloaderResponseResult {
1390        match response_message {
1391            FetchResponseMsg::ProcessRequestBody(..) => DownloaderResponseResult::InProcess,
1392            FetchResponseMsg::ProcessCspViolations(_request_id, violations) => {
1393                self.document_context
1394                    .csp_handler
1395                    .process_violations(violations);
1396                DownloaderResponseResult::InProcess
1397            },
1398            FetchResponseMsg::ProcessResponse(_, meta_result) => {
1399                trace!(
1400                    "@font-face {} metadata ok={:?}",
1401                    self.web_font_family_name,
1402                    meta_result.is_ok()
1403                );
1404                self.response_valid = meta_result.is_ok();
1405                DownloaderResponseResult::InProcess
1406            },
1407            FetchResponseMsg::ProcessResponseChunk(_, new_bytes) => {
1408                trace!(
1409                    "@font-face {} chunk={:?}",
1410                    self.web_font_family_name, new_bytes
1411                );
1412                if self.response_valid {
1413                    self.response_data.extend(new_bytes.0)
1414                }
1415                DownloaderResponseResult::InProcess
1416            },
1417            FetchResponseMsg::ProcessResponseEOF(_, response, timing) => {
1418                trace!(
1419                    "@font-face {} EOF={:?}",
1420                    self.web_font_family_name, response
1421                );
1422                if response.is_err() || !self.response_valid {
1423                    return DownloaderResponseResult::Failure;
1424                }
1425                self.document_context
1426                    .network_timing_handler
1427                    .submit_timing(ServoUrl::from_url(self.url.as_ref().clone()), timing);
1428                DownloaderResponseResult::Finished
1429            },
1430            FetchResponseMsg::ProcessContentLength(_request_id, size) => {
1431                self.response_data.reserve(size - self.response_data.len());
1432                DownloaderResponseResult::InProcess
1433            },
1434        }
1435    }
1436}
1437
1438#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
1439struct FontCacheKey {
1440    font_identifier: FontIdentifier,
1441    font_descriptor: FontDescriptor,
1442}
1443
1444#[derive(Debug, MallocSizeOf)]
1445struct FontGroupCacheKey {
1446    #[ignore_malloc_size_of = "This is also stored as part of styling."]
1447    style: ServoArc<FontStyleStruct>,
1448    size: Au,
1449}
1450
1451impl PartialEq for FontGroupCacheKey {
1452    fn eq(&self, other: &FontGroupCacheKey) -> bool {
1453        self.style == other.style && self.size == other.size
1454    }
1455}
1456
1457impl Eq for FontGroupCacheKey {}
1458
1459impl Hash for FontGroupCacheKey {
1460    fn hash<H>(&self, hasher: &mut H)
1461    where
1462        H: Hasher,
1463    {
1464        self.style.hash.hash(hasher)
1465    }
1466}
1467
1468#[derive(Default, MallocSizeOf)]
1469struct KnownFontFaceRules {
1470    /// Used to distinguish new, incoming `@font-face` rules from existing ones.
1471    ///
1472    /// Generations alternate between true and false, which is enough to tell one generation apart from
1473    /// the next.
1474    generation: bool,
1475    /// Maps from a font family name to a list of `@font-face` rules declaring fonts
1476    /// that belong to said family.
1477    contents: HashMap<Atom, Vec<KnownFontFaceRule>>,
1478}
1479
1480#[derive(MallocSizeOf)]
1481struct KnownFontFaceRule {
1482    rule_with_origin: FontFaceRuleWithOrigin,
1483    generation: bool,
1484}
1485
1486impl KnownFontFaceRules {
1487    /// Computes the difference between the `@font-face `rules that are currently in effect
1488    /// and the ones that the `Stylist` knows about. The caller is notified about new or removed rules
1489    /// with callbacks.
1490    fn diff_old_and_new_font_face_rules(
1491        &mut self,
1492        stylist: &Stylist,
1493        guards: &StylesheetGuards<'_>,
1494    ) -> WebFontSetDifference {
1495        let mut difference = WebFontSetDifference::default();
1496        self.generation = !self.generation;
1497
1498        let font_face_rules_in_cascade_order = stylist
1499            .iter_extra_data_origins()
1500            .flat_map(|(extra_data, origin)| {
1501                extra_data.font_faces.iter().rev().zip(iter::repeat(origin))
1502            })
1503            .map(|((rule, _layer), origin)| FontFaceRuleWithOrigin::new(rule.clone(), origin));
1504
1505        // First, find any *new* font families that were not defined previously
1506        let mut number_of_unchanged_rules = 0;
1507        let number_of_previously_known_rules: usize = self
1508            .contents
1509            .values()
1510            .map(|fonts_from_family| fonts_from_family.len())
1511            .sum();
1512        for rule_with_origin in font_face_rules_in_cascade_order {
1513            let borrowed_rule = rule_with_origin.read_with(guards);
1514
1515            let Some(font_family) = borrowed_rule.descriptors.font_family.as_ref() else {
1516                // Per https://github.com/w3c/csswg-drafts/issues/1133 an @font-face rule
1517                // is valid as far as the CSS parser is concerned even if it doesn’t have
1518                // a font-family or src declaration.
1519                // However, both are required for the rule to represent an actual font face.
1520                continue;
1521            };
1522            if borrowed_rule.descriptors.src.is_none() {
1523                // @font-face rules without a src don't constitute usable font faces.
1524                continue;
1525            }
1526
1527            let known_font_faces_for_family =
1528                self.contents.entry(font_family.name.clone()).or_default();
1529
1530            let mut conflicting_declaration_with_higher_priority_exists = false;
1531            let mut index_of_existing_entry_for_this_rule = None;
1532            for (index, known_font_face) in known_font_faces_for_family.iter().enumerate() {
1533                // See if this is a entry for this @font-face that existed prior to the current update
1534                if FontFaceRuleWithOrigin::ptr_eq(
1535                    &known_font_face.rule_with_origin,
1536                    &rule_with_origin,
1537                ) {
1538                    index_of_existing_entry_for_this_rule = Some(index);
1539                }
1540
1541                // Check if there are existing declarations with higher priority that conflict
1542                if conflicting_declaration_with_higher_priority_exists {
1543                    // We already found one conflict, no need to search for more.
1544                    continue;
1545                }
1546                if known_font_face.generation != self.generation {
1547                    // This rule was not inserted yet during this update, so it was either removed or
1548                    // has lower priority than the one currently being inserted.
1549                    continue;
1550                }
1551                if font_face_rules_conflict(
1552                    &known_font_face
1553                        .rule_with_origin
1554                        .read_with(guards)
1555                        .descriptors,
1556                    &borrowed_rule.descriptors,
1557                ) {
1558                    conflicting_declaration_with_higher_priority_exists = true;
1559                }
1560            }
1561
1562            if let Some(index_of_existing_entry_for_this_rule) =
1563                index_of_existing_entry_for_this_rule
1564            {
1565                // This @font-face rule was already present in the cascade prior to this update.
1566                // But if during this update we inserted a rule with higher priority that overrides this one
1567                // then we should not update its generation so it will be dropped at the end.
1568                if conflicting_declaration_with_higher_priority_exists {
1569                    let stale_rule =
1570                        known_font_faces_for_family.remove(index_of_existing_entry_for_this_rule);
1571                    difference
1572                        .removed_font_faces
1573                        .push(stale_rule.rule_with_origin);
1574                } else {
1575                    number_of_unchanged_rules += 1;
1576                    known_font_faces_for_family[index_of_existing_entry_for_this_rule].generation =
1577                        self.generation;
1578                }
1579            } else if conflicting_declaration_with_higher_priority_exists {
1580                // This (new) rule does not apply to the document because another rule with higher cascade priority
1581                // overrides it. We can simply ignore this declaration.
1582                continue;
1583            } else {
1584                // This is a new rule that does not conflict with anything that previously existed, so insert it.
1585                difference.added_font_faces.push(rule_with_origin.clone());
1586                known_font_faces_for_family.push(KnownFontFaceRule {
1587                    rule_with_origin,
1588                    generation: self.generation,
1589                });
1590            }
1591        }
1592
1593        if number_of_unchanged_rules == number_of_previously_known_rules {
1594            // This is the common case, where the new set of known @font-face rules is a superset of
1595            // the old one after applying the cascade. In this case there is nothing more to do,
1596            // because all old @font-face rules are still present.
1597            return difference;
1598        }
1599
1600        // Remove all `@font-face` rules that were not updated - those no longer exist on the stylist.
1601        self.contents.retain(|_, known_font_faces_for_family| {
1602            known_font_faces_for_family
1603                .extract_if(.., |rule| rule.generation != self.generation)
1604                .for_each(|removed_rule| {
1605                    difference
1606                        .removed_font_faces
1607                        .push(removed_rule.rule_with_origin);
1608                });
1609
1610            !known_font_faces_for_family.is_empty()
1611        });
1612
1613        difference
1614    }
1615}
1616
1617/// Returns `true` if the two `@font-face` rules cannot both apply at the same time.
1618///
1619/// Two font faces can coexist if they are different for the purposes of font matching:
1620/// <https://drafts.csswg.org/css-fonts-4/#font-matching-algorithm>
1621///
1622/// This method does assume that the family names have already been verified to be equal.
1623fn font_face_rules_conflict(
1624    first_rule: &FontFaceRuleDescriptors,
1625    second_rule: &FontFaceRuleDescriptors,
1626) -> bool {
1627    first_rule.font_stretch == second_rule.font_stretch &&
1628        first_rule.font_style == second_rule.font_style &&
1629        first_rule.font_weight == second_rule.font_weight &&
1630        first_rule.unicode_range == second_rule.unicode_range
1631}