1use std::collections::{HashMap, HashSet};
6use std::default::Default;
7use std::hash::{Hash, Hasher};
8use std::sync::Arc;
9use std::sync::atomic::{AtomicBool, Ordering};
10
11use app_units::Au;
12use content_security_policy::Violation;
13use fonts_traits::{
14 CSSFontFaceDescriptors, FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef,
15 FontTemplateRefMethods, StylesheetWebFontLoadFinishedCallback,
16};
17use log::{debug, trace};
18use malloc_size_of::MallocSizeOf;
19use malloc_size_of_derive::MallocSizeOf;
20use net_traits::blob_url_store::UrlWithBlobClaim;
21use net_traits::policy_container::PolicyContainer;
22use net_traits::request::{
23 CredentialsMode, Destination, Referrer, RequestBuilder, RequestClient, RequestMode,
24 ServiceWorkersMode,
25};
26use net_traits::{
27 CoreResourceThread, FetchResponseMsg, ResourceFetchTiming, ResourceThreads, fetch_async,
28};
29use paint_api::CrossProcessPaintApi;
30use parking_lot::{Mutex, RwLock};
31use rustc_hash::FxHashSet;
32use servo_arc::Arc as ServoArc;
33use servo_base::id::{PainterId, WebViewId};
34use servo_config::pref;
35use servo_url::ServoUrl;
36use style::Atom;
37use style::computed_values::font_variant_caps::T as FontVariantCaps;
38use style::device::Device;
39use style::font_face::{
40 FontFaceSourceFormat, FontFaceSourceFormatKeyword, Source, SourceList, UrlSource,
41};
42use style::properties::generated::font_face::Descriptors as FontFaceRuleDescriptors;
43use style::properties::style_structs::Font as FontStyleStruct;
44use style::shared_lock::SharedRwLockReadGuard;
45use style::stylesheets::{
46 CssRule, CustomMediaMap, DocumentStyleSheet, FontFaceRule, StylesheetInDocument,
47};
48use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFontFamily};
49use url::Url;
50use uuid::Uuid;
51use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
52
53use crate::font::{Font, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope};
54use crate::font_store::{CrossThreadFontStore, FontStore};
55use crate::platform::font::PlatformFont;
56use crate::{FontData, LowercaseFontFamilyName, PlatformFontMethods, SystemFontServiceProxy};
57
58static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; #[derive(Eq, Hash, MallocSizeOf, PartialEq)]
61pub(crate) struct FontParameters {
62 pub(crate) font_key: FontKey,
63 pub(crate) pt_size: Au,
64 pub(crate) variations: Vec<FontVariation>,
65 pub(crate) flags: FontInstanceFlags,
66}
67
68pub type FontGroupRef = Arc<FontGroup>;
69
70#[derive(MallocSizeOf)]
75pub struct FontContext {
76 #[conditional_malloc_size_of]
77 system_font_service_proxy: Arc<SystemFontServiceProxy>,
78
79 resource_threads: Mutex<CoreResourceThread>,
80
81 paint_api: Mutex<CrossProcessPaintApi>,
83
84 fonts: RwLock<HashMap<FontCacheKey, Option<FontRef>>>,
87
88 #[conditional_malloc_size_of]
92 resolved_font_groups: RwLock<HashMap<FontGroupCacheKey, FontGroupRef>>,
93
94 web_fonts: CrossThreadFontStore,
95
96 webrender_font_keys: RwLock<HashMap<FontIdentifier, FontKey>>,
99
100 webrender_font_instance_keys: RwLock<HashMap<FontParameters, FontInstanceKey>>,
103
104 font_data: RwLock<HashMap<FontIdentifier, FontData>>,
107
108 have_removed_web_fonts: AtomicBool,
109
110 currently_downloading_fonts: Mutex<HashMap<ServoUrl, Vec<WebFontDownloadState>>>,
113}
114
115pub trait CspViolationHandler: Send + std::fmt::Debug + MallocSizeOf {
119 fn process_violations(&self, violations: Vec<Violation>);
120 fn clone(&self) -> Box<dyn CspViolationHandler>;
121}
122
123pub trait NetworkTimingHandler: Send + std::fmt::Debug + MallocSizeOf {
126 fn submit_timing(&self, url: ServoUrl, response: ResourceFetchTiming);
127 fn clone(&self) -> Box<dyn NetworkTimingHandler>;
128}
129
130#[derive(Debug, MallocSizeOf)]
132pub struct WebFontDocumentContext {
133 pub policy_container: PolicyContainer,
134 pub request_client: RequestClient,
135 pub document_url: ServoUrl,
136 pub csp_handler: Box<dyn CspViolationHandler>,
137 pub network_timing_handler: Box<dyn NetworkTimingHandler>,
138}
139
140impl Clone for WebFontDocumentContext {
141 fn clone(&self) -> WebFontDocumentContext {
142 Self {
143 policy_container: self.policy_container.clone(),
144 request_client: self.request_client.clone(),
145 document_url: self.document_url.clone(),
146 csp_handler: self.csp_handler.clone(),
147 network_timing_handler: self.network_timing_handler.clone(),
148 }
149 }
150}
151
152impl FontContext {
153 pub fn new(
154 system_font_service_proxy: Arc<SystemFontServiceProxy>,
155 paint_api: CrossProcessPaintApi,
156 resource_threads: ResourceThreads,
157 ) -> Self {
158 Self {
159 system_font_service_proxy,
160 resource_threads: Mutex::new(resource_threads.core_thread),
161 paint_api: Mutex::new(paint_api),
162 fonts: Default::default(),
163 resolved_font_groups: Default::default(),
164 web_fonts: Default::default(),
165 webrender_font_keys: RwLock::default(),
166 webrender_font_instance_keys: RwLock::default(),
167 have_removed_web_fonts: AtomicBool::new(false),
168 font_data: RwLock::default(),
169 currently_downloading_fonts: Default::default(),
170 }
171 }
172
173 pub fn web_fonts_still_loading(&self) -> usize {
174 self.currently_downloading_fonts.lock().len()
175 }
176
177 fn get_font_data(&self, identifier: &FontIdentifier) -> Option<FontData> {
178 match identifier {
179 FontIdentifier::Web(_) | FontIdentifier::ArrayBuffer(_) => {
180 self.font_data.read().get(identifier).cloned()
181 },
182 FontIdentifier::Local(_) => None,
183 }
184 }
185
186 pub fn font_group(&self, style: ServoArc<FontStyleStruct>) -> FontGroupRef {
190 let font_size = style.font_size.computed_size().into();
191 self.font_group_with_size(style, font_size)
192 }
193
194 pub fn font_group_with_size(
197 &self,
198 style: ServoArc<FontStyleStruct>,
199 size: Au,
200 ) -> Arc<FontGroup> {
201 let cache_key = FontGroupCacheKey { size, style };
202 if let Some(font_group) = self.resolved_font_groups.read().get(&cache_key) {
203 return font_group.clone();
204 }
205
206 let mut descriptor = FontDescriptor::from(&*cache_key.style);
207 descriptor.pt_size = size;
208
209 let font_group = Arc::new(FontGroup::new(&cache_key.style, descriptor));
210 self.resolved_font_groups
211 .write()
212 .insert(cache_key, font_group.clone());
213 font_group
214 }
215
216 pub fn font(
219 &self,
220 font_template: FontTemplateRef,
221 font_descriptor: &FontDescriptor,
222 ) -> Option<FontRef> {
223 let font_descriptor = if servo_config::pref!(layout_variable_fonts_enabled) {
224 let variation_settings = font_template.borrow().compute_variations(font_descriptor);
225 &font_descriptor.with_variation_settings(variation_settings)
226 } else {
227 font_descriptor
228 };
229
230 self.get_font_maybe_synthesizing_small_caps(
231 font_template,
232 font_descriptor,
233 true, )
235 }
236
237 fn get_font_maybe_synthesizing_small_caps(
238 &self,
239 font_template: FontTemplateRef,
240 font_descriptor: &FontDescriptor,
241 synthesize_small_caps: bool,
242 ) -> Option<FontRef> {
243 let synthesized_small_caps_font =
247 if font_descriptor.variant == FontVariantCaps::SmallCaps && synthesize_small_caps {
248 let mut small_caps_descriptor = font_descriptor.clone();
249 small_caps_descriptor.pt_size =
250 font_descriptor.pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR);
251 self.get_font_maybe_synthesizing_small_caps(
252 font_template.clone(),
253 &small_caps_descriptor,
254 false, )
256 } else {
257 None
258 };
259
260 let cache_key = FontCacheKey {
261 font_identifier: font_template.identifier(),
262 font_descriptor: font_descriptor.clone(),
263 };
264
265 if let Some(font) = self.fonts.read().get(&cache_key).cloned() {
266 return font;
267 }
268
269 debug!(
270 "FontContext::font cache miss for font_template={:?} font_descriptor={:?}",
271 font_template, font_descriptor
272 );
273
274 let mut fonts = self.fonts.write();
279 if let Some(font) = fonts.get(&cache_key).cloned() {
280 return font;
281 }
282
283 let font = self
286 .create_font(
287 font_template,
288 font_descriptor.to_owned(),
289 synthesized_small_caps_font,
290 )
291 .ok();
292 fonts.insert(cache_key, font.clone());
293 font
294 }
295
296 fn matching_web_font_templates(
297 &self,
298 descriptor_to_match: &FontDescriptor,
299 family_descriptor: &FontFamilyDescriptor,
300 ) -> Option<Vec<FontTemplateRef>> {
301 if family_descriptor.scope != FontSearchScope::Any {
302 return None;
303 }
304
305 let SingleFontFamily::FamilyName(ref family_name) = family_descriptor.family else {
307 return None;
308 };
309
310 self.web_fonts
311 .read()
312 .families
313 .get(&family_name.name.clone().into())
314 .map(|templates| templates.find_for_descriptor(Some(descriptor_to_match)))
315 }
316
317 pub fn matching_templates(
320 &self,
321 descriptor_to_match: &FontDescriptor,
322 family_descriptor: &FontFamilyDescriptor,
323 ) -> Vec<FontTemplateRef> {
324 self.matching_web_font_templates(descriptor_to_match, family_descriptor)
325 .unwrap_or_else(|| {
326 self.system_font_service_proxy.find_matching_font_templates(
327 Some(descriptor_to_match),
328 &family_descriptor.family,
329 )
330 })
331 }
332
333 #[servo_tracing::instrument(skip_all)]
336 fn create_font(
337 &self,
338 font_template: FontTemplateRef,
339 font_descriptor: FontDescriptor,
340 synthesized_small_caps: Option<FontRef>,
341 ) -> Result<FontRef, &'static str> {
342 Ok(FontRef(Arc::new(Font::new(
343 font_template.clone(),
344 font_descriptor,
345 self.get_font_data(&font_template.identifier()),
346 synthesized_small_caps,
347 )?)))
348 }
349
350 pub(crate) fn create_font_instance_key(
351 &self,
352 font: &Font,
353 painter_id: PainterId,
354 ) -> FontInstanceKey {
355 match font.template.identifier() {
356 FontIdentifier::Local(_) => self.system_font_service_proxy.get_system_font_instance(
357 font.template.identifier(),
358 font.descriptor.pt_size,
359 font.webrender_font_instance_flags(),
360 font.variations().to_owned(),
361 painter_id,
362 ),
363 FontIdentifier::Web(_) | FontIdentifier::ArrayBuffer(_) => self
364 .create_web_font_instance(
365 font.template.clone(),
366 font.descriptor.pt_size,
367 font.webrender_font_instance_flags(),
368 font.variations().to_owned(),
369 painter_id,
370 ),
371 }
372 }
373
374 fn create_web_font_instance(
375 &self,
376 font_template: FontTemplateRef,
377 pt_size: Au,
378 flags: FontInstanceFlags,
379 variations: Vec<FontVariation>,
380 painter_id: PainterId,
381 ) -> FontInstanceKey {
382 let identifier = font_template.identifier();
383 let font_data = self
384 .get_font_data(&identifier)
385 .expect("Web font should have associated font data");
386 let font_key = *self
387 .webrender_font_keys
388 .write()
389 .entry(identifier.clone())
390 .or_insert_with(|| {
391 let font_key = self.system_font_service_proxy.generate_font_key(painter_id);
392 self.paint_api.lock().add_font(
393 font_key,
394 font_data.as_ipc_shared_memory(),
395 identifier.index(),
396 );
397 font_key
398 });
399
400 let entry_key = FontParameters {
401 font_key,
402 pt_size,
403 variations: variations.clone(),
404 flags,
405 };
406 *self
407 .webrender_font_instance_keys
408 .write()
409 .entry(entry_key)
410 .or_insert_with(|| {
411 let font_instance_key = self
412 .system_font_service_proxy
413 .generate_font_instance_key(painter_id);
414 self.paint_api.lock().add_font_instance(
415 font_instance_key,
416 font_key,
417 pt_size.to_f32_px(),
418 flags,
419 variations,
420 );
421 font_instance_key
422 })
423 }
424
425 fn invalidate_font_groups_after_web_font_load(&self) {
426 self.resolved_font_groups.write().clear();
427 }
428
429 pub fn is_supported_web_font_source(source: &&Source) -> bool {
430 let url_source = match &source {
431 Source::Url(url_source) => url_source,
432 Source::Local(_) => return true,
433 };
434 let format_hint = match url_source.format_hint {
435 Some(ref format_hint) => format_hint,
436 None => return true,
437 };
438
439 if matches!(
440 format_hint,
441 FontFaceSourceFormat::Keyword(
442 FontFaceSourceFormatKeyword::Truetype |
443 FontFaceSourceFormatKeyword::Opentype |
444 FontFaceSourceFormatKeyword::Woff |
445 FontFaceSourceFormatKeyword::Woff2
446 )
447 ) {
448 return true;
449 }
450
451 if let FontFaceSourceFormat::String(string) = format_hint {
452 if string == "truetype" || string == "opentype" || string == "woff" || string == "woff2"
453 {
454 return true;
455 }
456
457 return pref!(layout_variable_fonts_enabled) &&
458 (string == "truetype-variations" ||
459 string == "opentype-variations" ||
460 string == "woff-variations" ||
461 string == "woff2-variations");
462 }
463
464 false
465 }
466
467 fn is_local_or_unknown_url_font(
468 &self,
469 family_name: &LowercaseFontFamilyName,
470 source: &Source,
471 ) -> bool {
472 match source {
473 Source::Url(url) => !url
474 .url
475 .url()
476 .cloned()
477 .map(ServoUrl::from)
478 .map(FontIdentifier::Web)
479 .filter(|font_identifier| self.font_data.read().contains_key(font_identifier))
480 .is_some_and(|font_identifier| {
481 self.web_fonts
482 .read()
483 .families
484 .get(family_name)
485 .is_some_and(|templates| {
486 templates
487 .templates
488 .iter()
489 .any(|template| template.borrow().identifier == font_identifier)
490 })
491 }),
492 Source::Local(_) => true,
493 }
494 }
495
496 pub(crate) fn handle_web_font_request_started(
497 &self,
498 url: ServoUrl,
499 state: WebFontDownloadState,
500 ) {
501 self.currently_downloading_fonts
502 .lock()
503 .entry(url)
504 .or_default()
505 .push(state);
506 }
507
508 pub(crate) fn handle_web_font_request_succeeded(
514 &self,
515 font_data: FontData,
516 url: ServoUrl,
517 ) -> bool {
518 let Some(download_states) = self.currently_downloading_fonts.lock().remove(&url) else {
519 return false;
521 };
522 debug_assert!(
523 !download_states.is_empty(),
524 "Should have removed this entry"
525 );
526
527 let identifier = FontIdentifier::Web(url);
528 let Ok(handle) =
529 PlatformFont::new_from_data(identifier.clone(), &font_data, None, &[], false)
530 else {
531 return false;
532 };
533
534 self.font_data.write().insert(identifier.clone(), font_data);
535 let descriptor = handle.descriptor();
536 for download_state in download_states {
537 let mut descriptor = descriptor.clone();
538 descriptor.override_values_with_css_font_template_descriptors(
539 &download_state.css_font_face_descriptors,
540 );
541
542 let new_template = FontTemplate::new(
543 identifier.clone(),
544 descriptor,
545 download_state.initiator.font_face_rule().cloned(),
546 );
547
548 download_state.handle_web_font_load_success(new_template);
549 }
550
551 true
552 }
553
554 pub(crate) fn has_pending_font_requests_for_url(&self, url: ServoArc<Url>) -> bool {
555 self.currently_downloading_fonts
556 .lock()
557 .contains_key(&url.into())
558 }
559}
560
561#[derive(MallocSizeOf)]
564pub(crate) struct WebFontDownloadState {
565 webview_id: Option<WebViewId>,
566 css_font_face_descriptors: CSSFontFaceDescriptors,
567 remaining_sources: Vec<Source>,
568 local_fonts: HashMap<Atom, Option<FontTemplateRef>>,
569 #[conditional_malloc_size_of]
570 pub(crate) font_context: Arc<FontContext>,
571 initiator: WebFontLoadInitiator,
572 document_context: WebFontDocumentContext,
573}
574
575impl WebFontDownloadState {
576 fn new(
577 webview_id: Option<WebViewId>,
578 font_context: Arc<FontContext>,
579 css_font_face_descriptors: CSSFontFaceDescriptors,
580 initiator: WebFontLoadInitiator,
581 sources: Vec<Source>,
582 local_fonts: HashMap<Atom, Option<FontTemplateRef>>,
583 document_context: WebFontDocumentContext,
584 ) -> WebFontDownloadState {
585 WebFontDownloadState {
586 webview_id,
587 css_font_face_descriptors,
588 remaining_sources: sources,
589 local_fonts,
590 font_context,
591 initiator,
592 document_context,
593 }
594 }
595
596 pub(crate) fn handle_web_font_load_success(self, new_template: FontTemplate) {
597 let family_name = self.css_font_face_descriptors.family_name.clone();
598 match self.initiator {
599 WebFontLoadInitiator::Stylesheet(initiator) => {
600 self.font_context
601 .web_fonts
602 .write()
603 .add_new_template(family_name, new_template);
604 self.font_context
605 .invalidate_font_groups_after_web_font_load();
606 (initiator.callback)(true);
607 },
608 WebFontLoadInitiator::Script(callback) => {
609 callback(family_name, Some(new_template));
610 },
611 }
612 }
613
614 pub(crate) fn handle_web_font_load_failure(self) {
616 let family_name = self.css_font_face_descriptors.family_name.clone();
617 match self.initiator {
618 WebFontLoadInitiator::Stylesheet(initiator) => {
619 (initiator.callback)(false);
620 },
621 WebFontLoadInitiator::Script(callback) => {
622 callback(family_name, None);
623 },
624 }
625 }
626
627 fn was_created_for_font_face_rule(&self, font_face_rule: &FontFaceRuleDescriptors) -> bool {
628 self.initiator
629 .font_face_rule()
630 .is_some_and(|initiating_rule| initiating_rule == font_face_rule)
631 }
632}
633
634pub trait FontContextWebFontMethods {
635 fn add_all_web_fonts_from_stylesheet(
636 &self,
637 webview_id: WebViewId,
638 stylesheet: &DocumentStyleSheet,
639 guard: &SharedRwLockReadGuard,
640 device: &Device,
641 finished_callback: StylesheetWebFontLoadFinishedCallback,
642 document_context: &WebFontDocumentContext,
643 ) -> usize;
644 fn load_web_font_for_script(
645 &self,
646 webview_id: Option<WebViewId>,
647 sources: SourceList,
648 descriptors: CSSFontFaceDescriptors,
649 finished_callback: ScriptWebFontLoadFinishedCallback,
650 document_context: &WebFontDocumentContext,
651 );
652 fn handle_web_font_request_failed(&self, url: ServoUrl);
653}
654
655impl FontContextWebFontMethods for Arc<FontContext> {
656 fn add_all_web_fonts_from_stylesheet(
657 &self,
658 webview_id: WebViewId,
659 stylesheet: &DocumentStyleSheet,
660 guard: &SharedRwLockReadGuard,
661 device: &Device,
662 finished_callback: StylesheetWebFontLoadFinishedCallback,
663 document_context: &WebFontDocumentContext,
664 ) -> usize {
665 let mut number_loading = 0;
666 let custom_media = &CustomMediaMap::default();
667 for rule in stylesheet
668 .contents(guard)
669 .effective_rules(device, custom_media, guard)
670 {
671 let CssRule::FontFace(ref lock) = *rule else {
672 continue;
673 };
674
675 let rule: &FontFaceRule = lock.read_with(guard);
676
677 if rule.descriptors.font_family.is_none() {
682 continue;
683 }
684 let Some(ref sources) = rule.descriptors.src else {
685 continue;
686 };
687
688 let css_font_face_descriptors = rule.into();
689
690 let initiator = FontFaceRuleInitiator {
691 font_face_rule: rule.descriptors.clone(),
692 callback: finished_callback.clone(),
693 };
694
695 number_loading += 1;
696 self.start_loading_one_web_font(
697 Some(webview_id),
698 sources,
699 css_font_face_descriptors,
700 WebFontLoadInitiator::Stylesheet(Box::new(initiator)),
701 document_context,
702 );
703 }
704
705 number_loading
706 }
707
708 fn load_web_font_for_script(
709 &self,
710 webview_id: Option<WebViewId>,
711 sources: SourceList,
712 descriptors: CSSFontFaceDescriptors,
713 finished_callback: ScriptWebFontLoadFinishedCallback,
714 document_context: &WebFontDocumentContext,
715 ) {
716 let completion_handler = WebFontLoadInitiator::Script(finished_callback);
717 self.start_loading_one_web_font(
718 webview_id,
719 &sources,
720 descriptors,
721 completion_handler,
722 document_context,
723 );
724 }
725
726 fn handle_web_font_request_failed(&self, url: ServoUrl) {
728 let Some(subscribers) = self.currently_downloading_fonts.lock().remove(&url) else {
729 return;
730 };
731
732 for subscriber in subscribers {
733 self.process_next_web_font_source(subscriber);
734 }
735 }
736}
737
738impl FontContext {
739 pub fn collect_unused_webrender_resources(
740 &self,
741 all: bool,
742 ) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
743 if all {
744 let mut webrender_font_keys = self.webrender_font_keys.write();
745 let mut webrender_font_instance_keys = self.webrender_font_instance_keys.write();
746 self.have_removed_web_fonts.store(false, Ordering::Relaxed);
747 return (
748 webrender_font_keys.drain().map(|(_, key)| key).collect(),
749 webrender_font_instance_keys
750 .drain()
751 .map(|(_, key)| key)
752 .collect(),
753 );
754 }
755
756 if !self.have_removed_web_fonts.load(Ordering::Relaxed) {
757 return (Vec::new(), Vec::new());
758 }
759
760 let web_fonts = self.web_fonts.write();
762 let mut font_data = self.font_data.write();
763 let _fonts = self.fonts.write();
764 let _font_groups = self.resolved_font_groups.write();
765 let mut webrender_font_keys = self.webrender_font_keys.write();
766 let mut webrender_font_instance_keys = self.webrender_font_instance_keys.write();
767
768 let mut unused_identifiers: HashSet<FontIdentifier> =
769 webrender_font_keys.keys().cloned().collect();
770 for templates in web_fonts.families.values() {
771 templates.for_all_identifiers(|identifier| {
772 unused_identifiers.remove(identifier);
773 });
774 }
775
776 font_data.retain(|font_identifier, _| !unused_identifiers.contains(font_identifier));
777
778 self.have_removed_web_fonts.store(false, Ordering::Relaxed);
779
780 let mut removed_keys: FxHashSet<FontKey> = FxHashSet::default();
781 webrender_font_keys.retain(|identifier, font_key| {
782 if unused_identifiers.contains(identifier) {
783 removed_keys.insert(*font_key);
784 false
785 } else {
786 true
787 }
788 });
789
790 let mut removed_instance_keys: HashSet<FontInstanceKey> = HashSet::new();
791 webrender_font_instance_keys.retain(|font_param, instance_key| {
792 if removed_keys.contains(&font_param.font_key) {
793 removed_instance_keys.insert(*instance_key);
794 false
795 } else {
796 true
797 }
798 });
799
800 (
801 removed_keys.into_iter().collect(),
802 removed_instance_keys.into_iter().collect(),
803 )
804 }
805
806 fn remove_single_font_face_rule(
808 &self,
809 font_face_rule: &FontFaceRuleDescriptors,
810 font_store: &mut FontStore,
811 ) -> bool {
812 let Some(family) = font_face_rule.font_family.as_ref() else {
813 return false;
814 };
815
816 self.currently_downloading_fonts
818 .lock()
819 .retain(|_, download_states| {
820 download_states.retain(|download_state| {
821 !download_state.was_created_for_font_face_rule(font_face_rule)
822 });
823
824 !download_states.is_empty()
825 });
826
827 let lowercase_family_name: LowercaseFontFamilyName = family.name.clone().into();
828 let Some(known_family) = font_store.families.get_mut(&lowercase_family_name) else {
829 return false;
830 };
831 if !known_family.remove_template_for_font_face_rule(font_face_rule) {
832 return false;
833 }
834 self.fonts.write().retain(|_, font| match font {
835 Some(font) => !font
836 .template
837 .borrow()
838 .is_defined_by_font_face_rule(font_face_rule),
839 _ => true,
840 });
841
842 true
843 }
844
845 pub fn remove_all_web_fonts_from_stylesheet(
846 &self,
847 stylesheet: &DocumentStyleSheet,
848 device: &Device,
849 guard: &SharedRwLockReadGuard,
850 ) {
851 let mut web_fonts = self.web_fonts.write();
852
853 let mut removed_any = false;
857 let custom_media = &CustomMediaMap::default();
858 for rule in stylesheet
859 .contents(guard)
860 .effective_rules(device, custom_media, guard)
861 {
862 let CssRule::FontFace(ref lock) = *rule else {
863 continue;
864 };
865 let rule: &FontFaceRule = lock.read_with(guard);
866
867 removed_any |= self.remove_single_font_face_rule(&rule.descriptors, &mut web_fonts);
868 }
869
870 if !removed_any {
871 return;
872 };
873
874 self.resolved_font_groups.write().clear();
877
878 self.have_removed_web_fonts.store(true, Ordering::Relaxed);
880 }
881
882 pub fn add_template_to_font_context(
883 &self,
884 family_name: LowercaseFontFamilyName,
885 new_template: FontTemplate,
886 ) {
887 self.web_fonts
888 .write()
889 .add_new_template(family_name, new_template);
890 self.invalidate_font_groups_after_web_font_load();
891 }
892
893 pub fn construct_web_font_from_data(
894 &self,
895 data: &[u8],
896 descriptors: CSSFontFaceDescriptors,
897 ) -> Option<(LowercaseFontFamilyName, FontTemplate)> {
898 let bytes = fontsan::process(data)
899 .inspect_err(|error| {
900 debug!(
901 "Sanitiser rejected FontFace font: family={} with {error:?}",
902 descriptors.family_name,
903 );
904 })
905 .ok()?;
906 let font_data = FontData::from_bytes(&bytes);
907
908 let identifier = FontIdentifier::ArrayBuffer(Uuid::new_v4());
909 let handle =
910 PlatformFont::new_from_data(identifier.clone(), &font_data, None, &[], false).ok()?;
911
912 let new_template = FontTemplate::new(identifier.clone(), handle.descriptor(), None);
913
914 self.font_data.write().insert(identifier, font_data);
915
916 Some((descriptors.family_name, new_template))
917 }
918
919 fn start_loading_one_web_font(
920 self: &Arc<FontContext>,
921 webview_id: Option<WebViewId>,
922 source_list: &SourceList,
923 css_font_face_descriptors: CSSFontFaceDescriptors,
924 completion_handler: WebFontLoadInitiator,
925 document_context: &WebFontDocumentContext,
926 ) {
927 let sources: Vec<Source> = source_list
928 .0
929 .iter()
930 .rev()
931 .filter(Self::is_supported_web_font_source)
932 .filter(|source| {
933 self.is_local_or_unknown_url_font(&css_font_face_descriptors.family_name, source)
934 })
935 .cloned()
936 .collect();
937
938 let mut local_fonts = HashMap::new();
946 for source in sources.iter() {
947 if let Source::Local(family_name) = source {
948 local_fonts
949 .entry(family_name.name.clone())
950 .or_insert_with(|| {
951 let family = SingleFontFamily::FamilyName(FamilyName {
952 name: family_name.name.clone(),
953 syntax: FontFamilyNameSyntax::Quoted,
954 });
955 self.system_font_service_proxy
956 .find_matching_font_templates(None, &family)
957 .first()
958 .cloned()
959 });
960 }
961 }
962
963 self.process_next_web_font_source(WebFontDownloadState::new(
964 webview_id,
965 self.clone(),
966 css_font_face_descriptors,
967 completion_handler,
968 sources,
969 local_fonts,
970 document_context.clone(),
971 ));
972 }
973
974 pub(crate) fn process_next_web_font_source(
975 self: &Arc<FontContext>,
976 mut state: WebFontDownloadState,
977 ) {
978 let Some(source) = state.remaining_sources.pop() else {
979 state.handle_web_font_load_failure();
980 return;
981 };
982
983 let this = self.clone();
984 let web_font_family_name = state.css_font_face_descriptors.family_name.clone();
985 match source {
986 Source::Url(url_source) => {
987 RemoteWebFontDownloader::download(url_source, this, web_font_family_name, state)
988 },
989 Source::Local(ref local_family_name) => {
990 if let Some(new_template) = state
991 .local_fonts
992 .get(&local_family_name.name)
993 .cloned()
994 .flatten()
995 .and_then(|local_template| {
996 let template = FontTemplate::new_for_local_web_font(
997 local_template,
998 &state.css_font_face_descriptors,
999 state.initiator.font_face_rule().cloned(),
1000 )
1001 .ok()?;
1002 Some(template)
1003 })
1004 {
1005 state.handle_web_font_load_success(new_template);
1006 } else {
1007 this.process_next_web_font_source(state);
1008 }
1009 },
1010 }
1011 }
1012}
1013
1014pub(crate) type ScriptWebFontLoadFinishedCallback =
1015 Box<dyn FnOnce(LowercaseFontFamilyName, Option<FontTemplate>) + Send>;
1016
1017#[derive(MallocSizeOf)]
1018pub(crate) struct FontFaceRuleInitiator {
1019 font_face_rule: FontFaceRuleDescriptors,
1020 #[ignore_malloc_size_of = "dyn Fn"]
1021 callback: StylesheetWebFontLoadFinishedCallback,
1022}
1023
1024#[derive(MallocSizeOf)]
1025pub(crate) enum WebFontLoadInitiator {
1026 Stylesheet(Box<FontFaceRuleInitiator>),
1027 Script(#[ignore_malloc_size_of = "dyn Fn"] ScriptWebFontLoadFinishedCallback),
1028}
1029
1030impl WebFontLoadInitiator {
1031 pub(crate) fn font_face_rule(&self) -> Option<&FontFaceRuleDescriptors> {
1032 match self {
1033 Self::Stylesheet(initiator) => Some(&initiator.font_face_rule),
1034 Self::Script(_) => None,
1035 }
1036 }
1037}
1038
1039struct RemoteWebFontDownloader {
1040 url: ServoArc<Url>,
1042 web_font_family_name: LowercaseFontFamilyName,
1043 response_valid: bool,
1044 response_data: Vec<u8>,
1046 document_context: WebFontDocumentContext,
1047 font_context: Arc<FontContext>,
1048}
1049
1050enum DownloaderResponseResult {
1051 InProcess,
1052 Finished,
1053 Failure,
1054}
1055
1056impl RemoteWebFontDownloader {
1057 fn download(
1058 url_source: UrlSource,
1059 font_context: Arc<FontContext>,
1060 web_font_family_name: LowercaseFontFamilyName,
1061 state: WebFontDownloadState,
1062 ) {
1063 let url = match url_source.url.url() {
1065 Some(url) => url.clone(),
1066 None => return,
1067 };
1068
1069 let document_context = &state.document_context;
1070
1071 let request = RequestBuilder::new(
1072 state.webview_id,
1073 UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone().into()),
1074 Referrer::ReferrerUrl(document_context.document_url.clone()),
1075 )
1076 .destination(Destination::Font)
1077 .mode(RequestMode::CorsMode)
1078 .credentials_mode(CredentialsMode::CredentialsSameOrigin)
1079 .service_workers_mode(ServiceWorkersMode::All)
1080 .policy_container(document_context.policy_container.clone())
1081 .client(document_context.request_client.clone());
1082
1083 let core_resource_thread_clone = font_context.resource_threads.lock().clone();
1084
1085 debug!("Loading @font-face {} from {}", web_font_family_name, url);
1086 let mut downloader = Self {
1087 url: url.clone(),
1088 web_font_family_name,
1089 response_valid: false,
1090 response_data: Vec::new(),
1091 document_context: document_context.clone(),
1092 font_context: font_context.clone(),
1093 };
1094
1095 font_context.handle_web_font_request_started(url.into(), state);
1096 fetch_async(
1097 &core_resource_thread_clone,
1098 request,
1099 None,
1100 Box::new(move |response_message| {
1101 match downloader.handle_web_font_fetch_message(response_message) {
1102 DownloaderResponseResult::InProcess => {},
1103 DownloaderResponseResult::Finished => {
1104 downloader.process_downloaded_font_and_signal_completion()
1105 },
1106 DownloaderResponseResult::Failure => {
1107 font_context.handle_web_font_request_failed(downloader.url.clone().into());
1108 },
1109 }
1110 }),
1111 )
1112 }
1113
1114 fn process_downloaded_font_and_signal_completion(&mut self) {
1117 if !self
1120 .font_context
1121 .has_pending_font_requests_for_url(self.url.clone())
1122 {
1123 return;
1124 }
1125
1126 let font_data = std::mem::take(&mut self.response_data);
1127 trace!(
1128 "Downloaded @font-face {} ({} bytes)",
1129 self.web_font_family_name,
1130 font_data.len()
1131 );
1132
1133 let font_data = match fontsan::process(&font_data) {
1134 Ok(bytes) => FontData::from_bytes(&bytes),
1135 Err(error) => {
1136 debug!(
1137 "Sanitiser rejected web font url={:?} with {error:?}",
1138 self.url.as_str(),
1139 );
1140 return self
1141 .font_context
1142 .handle_web_font_request_failed(self.url.clone().into());
1143 },
1144 };
1145
1146 let url: ServoUrl = self.url.clone().into();
1147 self.font_context
1148 .handle_web_font_request_succeeded(font_data, url);
1149 }
1150
1151 fn handle_web_font_fetch_message(
1152 &mut self,
1153 response_message: FetchResponseMsg,
1154 ) -> DownloaderResponseResult {
1155 match response_message {
1156 FetchResponseMsg::ProcessRequestBody(..) => DownloaderResponseResult::InProcess,
1157 FetchResponseMsg::ProcessCspViolations(_request_id, violations) => {
1158 self.document_context
1159 .csp_handler
1160 .process_violations(violations);
1161 DownloaderResponseResult::InProcess
1162 },
1163 FetchResponseMsg::ProcessResponse(_, meta_result) => {
1164 trace!(
1165 "@font-face {} metadata ok={:?}",
1166 self.web_font_family_name,
1167 meta_result.is_ok()
1168 );
1169 self.response_valid = meta_result.is_ok();
1170 DownloaderResponseResult::InProcess
1171 },
1172 FetchResponseMsg::ProcessResponseChunk(_, new_bytes) => {
1173 trace!(
1174 "@font-face {} chunk={:?}",
1175 self.web_font_family_name, new_bytes
1176 );
1177 if self.response_valid {
1178 self.response_data.extend(new_bytes.0)
1179 }
1180 DownloaderResponseResult::InProcess
1181 },
1182 FetchResponseMsg::ProcessResponseEOF(_, response, timing) => {
1183 trace!(
1184 "@font-face {} EOF={:?}",
1185 self.web_font_family_name, response
1186 );
1187 if response.is_err() || !self.response_valid {
1188 return DownloaderResponseResult::Failure;
1189 }
1190 self.document_context
1191 .network_timing_handler
1192 .submit_timing(ServoUrl::from_url(self.url.as_ref().clone()), timing);
1193 DownloaderResponseResult::Finished
1194 },
1195 }
1196 }
1197}
1198
1199#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
1200struct FontCacheKey {
1201 font_identifier: FontIdentifier,
1202 font_descriptor: FontDescriptor,
1203}
1204
1205#[derive(Debug, MallocSizeOf)]
1206struct FontGroupCacheKey {
1207 #[ignore_malloc_size_of = "This is also stored as part of styling."]
1208 style: ServoArc<FontStyleStruct>,
1209 size: Au,
1210}
1211
1212impl PartialEq for FontGroupCacheKey {
1213 fn eq(&self, other: &FontGroupCacheKey) -> bool {
1214 self.style == other.style && self.size == other.size
1215 }
1216}
1217
1218impl Eq for FontGroupCacheKey {}
1219
1220impl Hash for FontGroupCacheKey {
1221 fn hash<H>(&self, hasher: &mut H)
1222 where
1223 H: Hasher,
1224 {
1225 self.style.hash.hash(hasher)
1226 }
1227}