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