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