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