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