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