/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::collections::{HashMap, HashSet}; use std::default::Default; use std::hash::{BuildHasherDefault, Hash, Hasher}; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use app_units::Au; use base::id::WebViewId; use fnv::FnvHasher; use fonts_traits::StylesheetWebFontLoadFinishedCallback; use log::{debug, trace}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of_derive::MallocSizeOf; use net_traits::request::{Destination, Referrer, RequestBuilder}; use net_traits::{CoreResourceThread, FetchResponseMsg, ResourceThreads, fetch_async}; use parking_lot::{Mutex, RwLock}; use servo_arc::Arc as ServoArc; use servo_url::ServoUrl; use style::Atom; use style::computed_values::font_variant_caps::T as FontVariantCaps; use style::font_face::{ FontFaceSourceFormat, FontFaceSourceFormatKeyword, Source, SourceList, UrlSource, }; use style::media_queries::Device; use style::properties::style_structs::Font as FontStyleStruct; use style::shared_lock::SharedRwLockReadGuard; use style::stylesheets::{CssRule, DocumentStyleSheet, FontFaceRule, StylesheetInDocument}; use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFontFamily}; use url::Url; use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey}; use webrender_traits::CrossProcessCompositorApi; use crate::font::{ Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope, }; use crate::font_store::CrossThreadFontStore; use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods}; use crate::platform::font::PlatformFont; use crate::system_font_service::{CSSFontFaceDescriptors, FontIdentifier}; use crate::{FontData, LowercaseFontFamilyName, PlatformFontMethods, SystemFontServiceProxy}; static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h) /// The FontContext represents the per-thread/thread state necessary for /// working with fonts. It is the public API used by the layout and /// paint code. It talks directly to the system font service where /// required. pub struct FontContext { system_font_service_proxy: Arc, resource_threads: Mutex, /// A sender that can send messages and receive replies from the compositor. compositor_api: Mutex, /// The actual instances of fonts ie a [`FontTemplate`] combined with a size and /// other font properties, along with the font data and a platform font instance. fonts: RwLock>>, /// A caching map between the specification of a font in CSS style and /// resolved [`FontGroup`] which contains information about all fonts that /// can be selected with that style. resolved_font_groups: RwLock>, BuildHasherDefault>>, web_fonts: CrossThreadFontStore, /// A collection of WebRender [`FontKey`]s generated for the web fonts that this /// [`FontContext`] controls. webrender_font_keys: RwLock>, /// A collection of WebRender [`FontInstanceKey`]s generated for the web fonts that /// this [`FontContext`] controls. webrender_font_instance_keys: RwLock>, /// The data for each web font [`FontIdentifier`]. This data might be used by more than one /// [`FontTemplate`] as each identifier refers to a URL. font_data: RwLock>, have_removed_web_fonts: AtomicBool, } impl MallocSizeOf for FontContext { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { let font_cache_size = self .fonts .read() .iter() .map(|(key, font)| { key.size_of(ops) + font.as_ref().map_or(0, |font| (*font).size_of(ops)) }) .sum::(); let font_group_cache_size = self .resolved_font_groups .read() .iter() .map(|(key, font_group)| key.size_of(ops) + (*font_group.read()).size_of(ops)) .sum::(); font_cache_size + font_group_cache_size } } impl FontContext { pub fn new( system_font_service_proxy: Arc, compositor_api: CrossProcessCompositorApi, resource_threads: ResourceThreads, ) -> Self { #[allow(clippy::default_constructed_unit_structs)] Self { system_font_service_proxy, resource_threads: Mutex::new(resource_threads.core_thread), compositor_api: Mutex::new(compositor_api), fonts: Default::default(), resolved_font_groups: Default::default(), web_fonts: Arc::new(RwLock::default()), webrender_font_keys: RwLock::default(), webrender_font_instance_keys: RwLock::default(), have_removed_web_fonts: AtomicBool::new(false), font_data: RwLock::default(), } } pub fn web_fonts_still_loading(&self) -> usize { self.web_fonts.read().number_of_fonts_still_loading() } fn get_font_data(&self, identifier: &FontIdentifier) -> Option { match identifier { FontIdentifier::Web(_) => self.font_data.read().get(identifier).cloned(), FontIdentifier::Local(_) => None, } } /// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`. /// Font groups are cached, so subsequent calls with the same `style` will return a reference /// to an existing `FontGroup`. pub fn font_group(&self, style: ServoArc) -> Arc> { let font_size = style.font_size.computed_size().into(); self.font_group_with_size(style, font_size) } /// Like [`Self::font_group`], but overriding the size found in the [`FontStyleStruct`] with the given size /// in pixels. pub fn font_group_with_size( &self, style: ServoArc, size: Au, ) -> Arc> { let cache_key = FontGroupCacheKey { size, style }; if let Some(font_group) = self.resolved_font_groups.read().get(&cache_key) { return font_group.clone(); } let mut descriptor = FontDescriptor::from(&*cache_key.style); descriptor.pt_size = size; let font_group = Arc::new(RwLock::new(FontGroup::new(&cache_key.style, descriptor))); self.resolved_font_groups .write() .insert(cache_key, font_group.clone()); font_group } /// Returns a font matching the parameters. Fonts are cached, so repeated calls will return a /// reference to the same underlying `Font`. pub fn font( &self, font_template: FontTemplateRef, font_descriptor: &FontDescriptor, ) -> Option { self.get_font_maybe_synthesizing_small_caps( font_template, font_descriptor, true, /* synthesize_small_caps */ ) } fn get_font_maybe_synthesizing_small_caps( &self, font_template: FontTemplateRef, font_descriptor: &FontDescriptor, synthesize_small_caps: bool, ) -> Option { // TODO: (Bug #3463): Currently we only support fake small-caps // painting. We should also support true small-caps (where the // font supports it) in the future. let synthesized_small_caps_font = if font_descriptor.variant == FontVariantCaps::SmallCaps && synthesize_small_caps { let mut small_caps_descriptor = font_descriptor.clone(); small_caps_descriptor.pt_size = font_descriptor.pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR); self.get_font_maybe_synthesizing_small_caps( font_template.clone(), &small_caps_descriptor, false, /* synthesize_small_caps */ ) } else { None }; let cache_key = FontCacheKey { font_identifier: font_template.identifier(), font_descriptor: font_descriptor.clone(), }; if let Some(font) = self.fonts.read().get(&cache_key).cloned() { return font; } debug!( "FontContext::font cache miss for font_template={:?} font_descriptor={:?}", font_template, font_descriptor ); // TODO: Inserting `None` into the cache here is a bit bogus. Instead we should somehow // mark this template as invalid so it isn't tried again. let font = self .create_font( font_template, font_descriptor.to_owned(), synthesized_small_caps_font, ) .ok(); self.fonts.write().insert(cache_key, font.clone()); font } fn matching_web_font_templates( &self, descriptor_to_match: &FontDescriptor, family_descriptor: &FontFamilyDescriptor, ) -> Option> { if family_descriptor.scope != FontSearchScope::Any { return None; } // Do not look for generic fonts in our list of web fonts. let SingleFontFamily::FamilyName(ref family_name) = family_descriptor.family else { return None; }; self.web_fonts .read() .families .get(&family_name.name.clone().into()) .map(|templates| templates.find_for_descriptor(Some(descriptor_to_match))) } /// Try to find matching templates in this [`FontContext`], first looking in the list of web fonts and /// falling back to asking the [`super::SystemFontService`] for a matching system font. pub fn matching_templates( &self, descriptor_to_match: &FontDescriptor, family_descriptor: &FontFamilyDescriptor, ) -> Vec { self.matching_web_font_templates(descriptor_to_match, family_descriptor) .unwrap_or_else(|| { self.system_font_service_proxy.find_matching_font_templates( Some(descriptor_to_match), &family_descriptor.family, ) }) } /// Create a `Font` for use in layout calculations, from a `FontTemplateData` returned by the /// cache thread and a `FontDescriptor` which contains the styling parameters. #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] fn create_font( &self, font_template: FontTemplateRef, font_descriptor: FontDescriptor, synthesized_small_caps: Option, ) -> Result { Ok(Arc::new(Font::new( font_template.clone(), font_descriptor.clone(), self.get_font_data(&font_template.identifier()), synthesized_small_caps, )?)) } pub(crate) fn create_font_instance_key(&self, font: &Font) -> FontInstanceKey { match font.template.identifier() { FontIdentifier::Local(_) => self.system_font_service_proxy.get_system_font_instance( font.template.identifier(), font.descriptor.pt_size, font.webrender_font_instance_flags(), ), FontIdentifier::Web(_) => self.create_web_font_instance( font.template.clone(), font.descriptor.pt_size, font.webrender_font_instance_flags(), ), } } fn create_web_font_instance( &self, font_template: FontTemplateRef, pt_size: Au, flags: FontInstanceFlags, ) -> FontInstanceKey { let identifier = font_template.identifier().clone(); let font_data = self .get_font_data(&identifier) .expect("Web font should have associated font data"); let font_key = *self .webrender_font_keys .write() .entry(identifier.clone()) .or_insert_with(|| { let font_key = self.system_font_service_proxy.generate_font_key(); self.compositor_api.lock().add_font( font_key, font_data.as_ipc_shared_memory(), identifier.index(), ); font_key }); let key = *self .webrender_font_instance_keys .write() .entry((font_key, pt_size)) .or_insert_with(|| { let font_instance_key = self.system_font_service_proxy.generate_font_instance_key(); self.compositor_api.lock().add_font_instance( font_instance_key, font_key, pt_size.to_f32_px(), flags, ); font_instance_key }); key } fn invalidate_font_groups_after_web_font_load(&self) { self.resolved_font_groups.write().clear(); } pub fn is_supported_web_font_source(source: &&Source) -> bool { let url_source = match source { Source::Url(ref url_source) => url_source, Source::Local(_) => return true, }; let format_hint = match url_source.format_hint { Some(ref format_hint) => format_hint, None => return true, }; if matches!( format_hint, FontFaceSourceFormat::Keyword( FontFaceSourceFormatKeyword::Truetype | FontFaceSourceFormatKeyword::Opentype | FontFaceSourceFormatKeyword::Woff | FontFaceSourceFormatKeyword::Woff2 ) ) { return true; } if let FontFaceSourceFormat::String(string) = format_hint { return string == "truetype" || string == "opentype" || string == "woff" || string == "woff2"; } false } } pub(crate) struct WebFontDownloadState { webview_id: Option, css_font_face_descriptors: CSSFontFaceDescriptors, remaining_sources: Vec, core_resource_thread: CoreResourceThread, local_fonts: HashMap>, font_context: Arc, initiator: WebFontLoadInitiator, } impl WebFontDownloadState { fn new( webview_id: Option, font_context: Arc, css_font_face_descriptors: CSSFontFaceDescriptors, initiator: WebFontLoadInitiator, sources: Vec, local_fonts: HashMap>, ) -> WebFontDownloadState { match initiator { WebFontLoadInitiator::Stylesheet(ref stylesheet, _) => { font_context .web_fonts .write() .handle_web_font_load_started_for_stylesheet(stylesheet); }, WebFontLoadInitiator::Script(_) => { font_context .web_fonts .write() .handle_web_font_load_started_for_script(); }, }; let core_resource_thread = font_context.resource_threads.lock().clone(); WebFontDownloadState { webview_id, css_font_face_descriptors, remaining_sources: sources, core_resource_thread, local_fonts, font_context, initiator, } } fn handle_web_font_load_success(self, new_template: FontTemplate) { let family_name = self.css_font_face_descriptors.family_name.clone(); match self.initiator { WebFontLoadInitiator::Stylesheet(ref stylesheet, ref callback) => { let not_cancelled = self .font_context .web_fonts .write() .handle_web_font_loaded_for_stylesheet(stylesheet, family_name, new_template); self.font_context .invalidate_font_groups_after_web_font_load(); callback(not_cancelled); }, WebFontLoadInitiator::Script(callback) => { self.font_context .web_fonts .write() .handle_web_font_load_finished_for_script(); callback(family_name, Some(new_template)); }, } } fn handle_web_font_load_failure(self) { let family_name = self.css_font_face_descriptors.family_name.clone(); match self.initiator { WebFontLoadInitiator::Stylesheet(ref stylesheet, ref callback) => { self.font_context .web_fonts .write() .handle_web_font_load_failed_for_stylesheet(stylesheet); callback(false); }, WebFontLoadInitiator::Script(callback) => { self.font_context .web_fonts .write() .handle_web_font_load_finished_for_script(); callback(family_name, None); }, } } fn font_load_cancelled(&self) -> bool { match self.initiator { WebFontLoadInitiator::Stylesheet(ref stylesheet, _) => self .font_context .web_fonts .read() .font_load_cancelled_for_stylesheet(stylesheet), WebFontLoadInitiator::Script(_) => false, } } } pub trait FontContextWebFontMethods { fn add_all_web_fonts_from_stylesheet( &self, webview_id: WebViewId, stylesheet: &DocumentStyleSheet, guard: &SharedRwLockReadGuard, device: &Device, finished_callback: StylesheetWebFontLoadFinishedCallback, ) -> usize; fn load_web_font_for_script( &self, webview_id: Option, source_list: SourceList, descriptors: CSSFontFaceDescriptors, finished_callback: ScriptWebFontLoadFinishedCallback, ); fn add_template_to_font_context( &self, family_name: LowercaseFontFamilyName, font_template: FontTemplate, ); fn remove_all_web_fonts_from_stylesheet(&self, stylesheet: &DocumentStyleSheet); fn collect_unused_webrender_resources(&self, all: bool) -> (Vec, Vec); } impl FontContextWebFontMethods for Arc { fn add_all_web_fonts_from_stylesheet( &self, webview_id: WebViewId, stylesheet: &DocumentStyleSheet, guard: &SharedRwLockReadGuard, device: &Device, finished_callback: StylesheetWebFontLoadFinishedCallback, ) -> usize { let mut number_loading = 0; for rule in stylesheet.effective_rules(device, guard) { let CssRule::FontFace(ref lock) = *rule else { continue; }; let rule: &FontFaceRule = lock.read_with(guard); let Some(font_face) = rule.font_face() else { continue; }; let css_font_face_descriptors = rule.into(); let completion_handler = WebFontLoadInitiator::Stylesheet(stylesheet.clone(), finished_callback.clone()); number_loading += 1; self.start_loading_one_web_font( Some(webview_id), font_face.sources(), css_font_face_descriptors, completion_handler, ); } number_loading } fn remove_all_web_fonts_from_stylesheet(&self, stylesheet: &DocumentStyleSheet) { let mut web_fonts = self.web_fonts.write(); let mut fonts = self.fonts.write(); let mut font_groups = self.resolved_font_groups.write(); // Cancel any currently in-progress web font loads. web_fonts.handle_stylesheet_removed(stylesheet); let mut removed_any = false; for family in web_fonts.families.values_mut() { removed_any |= family.remove_templates_for_stylesheet(stylesheet); } if !removed_any { return; }; fonts.retain(|_, font| match font { Some(font) => font.template.borrow().stylesheet.as_ref() != Some(stylesheet), _ => true, }); // Removing this stylesheet modified the available fonts, so invalidate the cache // of resolved font groups. font_groups.clear(); // Ensure that we clean up any WebRender resources on the next display list update. self.have_removed_web_fonts.store(true, Ordering::Relaxed); } fn collect_unused_webrender_resources( &self, all: bool, ) -> (Vec, Vec) { if all { let mut webrender_font_keys = self.webrender_font_keys.write(); let mut webrender_font_instance_keys = self.webrender_font_instance_keys.write(); self.have_removed_web_fonts.store(false, Ordering::Relaxed); return ( webrender_font_keys.drain().map(|(_, key)| key).collect(), webrender_font_instance_keys .drain() .map(|(_, key)| key) .collect(), ); } if !self.have_removed_web_fonts.load(Ordering::Relaxed) { return (Vec::new(), Vec::new()); } // Lock everything to prevent adding new fonts while we are cleaning up the old ones. let web_fonts = self.web_fonts.write(); let mut font_data = self.font_data.write(); let _fonts = self.fonts.write(); let _font_groups = self.resolved_font_groups.write(); let mut webrender_font_keys = self.webrender_font_keys.write(); let mut webrender_font_instance_keys = self.webrender_font_instance_keys.write(); let mut unused_identifiers: HashSet = webrender_font_keys.keys().cloned().collect(); for templates in web_fonts.families.values() { templates.for_all_identifiers(|identifier| { unused_identifiers.remove(identifier); }); } font_data.retain(|font_identifier, _| !unused_identifiers.contains(font_identifier)); self.have_removed_web_fonts.store(false, Ordering::Relaxed); let mut removed_keys: HashSet = HashSet::new(); webrender_font_keys.retain(|identifier, font_key| { if unused_identifiers.contains(identifier) { removed_keys.insert(*font_key); false } else { true } }); let mut removed_instance_keys: HashSet = HashSet::new(); webrender_font_instance_keys.retain(|(font_key, _), instance_key| { if removed_keys.contains(font_key) { removed_instance_keys.insert(*instance_key); false } else { true } }); ( removed_keys.into_iter().collect(), removed_instance_keys.into_iter().collect(), ) } fn load_web_font_for_script( &self, webview_id: Option, sources: SourceList, descriptors: CSSFontFaceDescriptors, finished_callback: ScriptWebFontLoadFinishedCallback, ) { let completion_handler = WebFontLoadInitiator::Script(finished_callback); self.start_loading_one_web_font(webview_id, &sources, descriptors, completion_handler); } fn add_template_to_font_context( &self, family_name: LowercaseFontFamilyName, new_template: FontTemplate, ) { self.web_fonts .write() .add_new_template(family_name, new_template); self.invalidate_font_groups_after_web_font_load(); } } impl FontContext { fn start_loading_one_web_font( self: &Arc, webview_id: Option, source_list: &SourceList, css_font_face_descriptors: CSSFontFaceDescriptors, completion_handler: WebFontLoadInitiator, ) { let sources: Vec = source_list .0 .iter() .rev() .filter(Self::is_supported_web_font_source) .cloned() .collect(); // Fetch all local fonts first, beacause if we try to fetch them later on during the process of // loading the list of web font `src`s we may be running in the context of the router thread, which // means we won't be able to seend IPC messages to the FontCacheThread. // // TODO: This is completely wrong. The specification says that `local()` font-family should match // against full PostScript names, but this is matching against font family names. This works... // sometimes. let mut local_fonts = HashMap::new(); for source in sources.iter() { if let Source::Local(family_name) = source { local_fonts .entry(family_name.name.clone()) .or_insert_with(|| { let family = SingleFontFamily::FamilyName(FamilyName { name: family_name.name.clone(), syntax: FontFamilyNameSyntax::Quoted, }); self.system_font_service_proxy .find_matching_font_templates(None, &family) .first() .cloned() }); } } self.process_next_web_font_source(WebFontDownloadState::new( webview_id, self.clone(), css_font_face_descriptors, completion_handler, sources, local_fonts, )); } fn process_next_web_font_source(self: &Arc, mut state: WebFontDownloadState) { let Some(source) = state.remaining_sources.pop() else { state.handle_web_font_load_failure(); return; }; let this = self.clone(); let web_font_family_name = state.css_font_face_descriptors.family_name.clone(); match source { Source::Url(url_source) => { RemoteWebFontDownloader::download(url_source, this, web_font_family_name, state) }, Source::Local(ref local_family_name) => { if let Some(new_template) = state .local_fonts .get(&local_family_name.name) .cloned() .flatten() .and_then(|local_template| { let template = FontTemplate::new_for_local_web_font( local_template.clone(), &state.css_font_face_descriptors, state.initiator.stylesheet().cloned(), ) .ok()?; Some(template) }) { state.handle_web_font_load_success(new_template); } else { this.process_next_web_font_source(state); } }, } } } pub type ScriptWebFontLoadFinishedCallback = Box) + Send>; pub(crate) enum WebFontLoadInitiator { Stylesheet(DocumentStyleSheet, StylesheetWebFontLoadFinishedCallback), Script(ScriptWebFontLoadFinishedCallback), } impl WebFontLoadInitiator { pub(crate) fn stylesheet(&self) -> Option<&DocumentStyleSheet> { match self { Self::Stylesheet(stylesheet, _) => Some(stylesheet), Self::Script(_) => None, } } } struct RemoteWebFontDownloader { state: Option, url: ServoArc, web_font_family_name: LowercaseFontFamilyName, response_valid: bool, response_data: Vec, } enum DownloaderResponseResult { InProcess, Finished, Failure, } impl RemoteWebFontDownloader { fn download( url_source: UrlSource, font_context: Arc, web_font_family_name: LowercaseFontFamilyName, state: WebFontDownloadState, ) { // https://drafts.csswg.org/css-fonts/#font-fetching-requirements let url = match url_source.url.url() { Some(url) => url.clone(), None => return, }; // FIXME: This shouldn't use NoReferrer, but the current documents url let request = RequestBuilder::new(state.webview_id, url.clone().into(), Referrer::NoReferrer) .destination(Destination::Font); let core_resource_thread_clone = state.core_resource_thread.clone(); debug!("Loading @font-face {} from {}", web_font_family_name, url); let mut downloader = Self { url, web_font_family_name, response_valid: false, response_data: Vec::new(), state: Some(state), }; fetch_async( &core_resource_thread_clone, request, None, Box::new(move |response_message| { match downloader.handle_web_font_fetch_message(response_message) { DownloaderResponseResult::InProcess => {}, DownloaderResponseResult::Finished => { if !downloader.process_downloaded_font_and_signal_completion() { font_context.process_next_web_font_source(downloader.take_state()) } }, DownloaderResponseResult::Failure => { font_context.process_next_web_font_source(downloader.take_state()) }, } }), ) } fn take_state(&mut self) -> WebFontDownloadState { self.state .take() .expect("must be non-None until download either succeeds or fails") } /// After a download finishes, try to process the downloaded data, returning true if /// the font is added successfully to the [`FontContext`] or false if it isn't. fn process_downloaded_font_and_signal_completion(&mut self) -> bool { let state = self .state .as_ref() .expect("must be non-None until processing is completed"); if state.font_load_cancelled() { self.take_state().handle_web_font_load_failure(); // Returning true here prevents trying to load the next font on the source list. return true; } let font_data = std::mem::take(&mut self.response_data); trace!( "Downloaded @font-face {} ({} bytes)", self.web_font_family_name, font_data.len() ); let font_data = match fontsan::process(&font_data) { Ok(bytes) => FontData::from_bytes(&bytes), Err(error) => { debug!( "Sanitiser rejected web font: family={} url={:?} with {error:?}", self.web_font_family_name, self.url, ); return false; }, }; let url: ServoUrl = self.url.clone().into(); let identifier = FontIdentifier::Web(url.clone()); let Ok(handle) = PlatformFont::new_from_data(identifier, &font_data, None) else { return false; }; let state = self.take_state(); let mut descriptor = handle.descriptor(); descriptor .override_values_with_css_font_template_descriptors(&state.css_font_face_descriptors); let new_template = FontTemplate::new( FontIdentifier::Web(url), descriptor, state.initiator.stylesheet().cloned(), ); state .font_context .font_data .write() .insert(new_template.identifier.clone(), font_data); state.handle_web_font_load_success(new_template); // If the load was canceled above, then we still want to return true from this function in // order to halt any attempt to load sources that come later on the source list. true } fn handle_web_font_fetch_message( &mut self, response_message: FetchResponseMsg, ) -> DownloaderResponseResult { match response_message { FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => { DownloaderResponseResult::InProcess }, FetchResponseMsg::ProcessResponse(_, meta_result) => { trace!( "@font-face {} metadata ok={:?}", self.web_font_family_name, meta_result.is_ok() ); self.response_valid = meta_result.is_ok(); DownloaderResponseResult::InProcess }, FetchResponseMsg::ProcessResponseChunk(_, new_bytes) => { trace!( "@font-face {} chunk={:?}", self.web_font_family_name, new_bytes ); if self.response_valid { self.response_data.extend(new_bytes) } DownloaderResponseResult::InProcess }, FetchResponseMsg::ProcessResponseEOF(_, response) => { trace!( "@font-face {} EOF={:?}", self.web_font_family_name, response ); if response.is_err() || !self.response_valid { return DownloaderResponseResult::Failure; } DownloaderResponseResult::Finished }, } } } #[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)] struct FontCacheKey { font_identifier: FontIdentifier, font_descriptor: FontDescriptor, } #[derive(Debug, MallocSizeOf)] struct FontGroupCacheKey { #[ignore_malloc_size_of = "This is also stored as part of styling."] style: ServoArc, size: Au, } impl PartialEq for FontGroupCacheKey { fn eq(&self, other: &FontGroupCacheKey) -> bool { self.style == other.style && self.size == other.size } } impl Eq for FontGroupCacheKey {} impl Hash for FontGroupCacheKey { fn hash(&self, hasher: &mut H) where H: Hasher, { self.style.hash.hash(hasher) } }