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