diff options
author | Martin Robinson <mrobinson@igalia.com> | 2024-05-20 16:13:03 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-20 14:13:03 +0000 |
commit | be5b527ea34c8be1bfa53d4a4eaf2729e100db28 (patch) | |
tree | 371df7732a9c095e268fc3c6a9321672dc515922 | |
parent | 8d2d955bbb23826b75faf866e4dcccabb8d7f4e8 (diff) | |
download | servo-be5b527ea34c8be1bfa53d4a4eaf2729e100db28.tar.gz servo-be5b527ea34c8be1bfa53d4a4eaf2729e100db28.zip |
fonts: Store web fonts in the per-Layout `FontContext` (#32303)
This moves mangement of web fonts to the per-Layout `FontContext`,
preventing web fonts from being available in different Documents.
Fixes #12920.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
30 files changed, 1009 insertions, 557 deletions
diff --git a/Cargo.lock b/Cargo.lock index 9ccee76fa66..a4992641d67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -639,6 +639,7 @@ dependencies = [ "ipc-channel", "log", "lyon_geom", + "net_traits", "num-traits", "pathfinder_geometry", "pixels", @@ -2051,6 +2052,7 @@ dependencies = [ "unicode-bidi", "unicode-properties", "unicode-script", + "url", "webrender_api", "webrender_traits", "xi-unicode", @@ -2062,6 +2064,7 @@ dependencies = [ name = "gfx_traits" version = "0.0.1" dependencies = [ + "ipc-channel", "malloc_size_of", "malloc_size_of_derive", "range", diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 46c78b8ea08..3a56aba1024 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -29,6 +29,7 @@ half = "2" ipc-channel = { workspace = true } log = { workspace = true } lyon_geom = "1.0.4" +net_traits = { workspace = true } num-traits = { workspace = true } pathfinder_geometry = "0.5" pixels = { path = "../pixels" } diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index 63f881ddacf..2ace98e9f1f 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -16,6 +16,7 @@ use gfx::font_context::FontContext; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use log::warn; +use net_traits::ResourceThreads; use webrender_api::ImageKey; use webrender_traits::ImageUpdate; @@ -44,12 +45,13 @@ impl<'a> CanvasPaintThread<'a> { fn new( webrender_api: Box<dyn WebrenderApi>, font_cache_thread: FontCacheThread, + resource_threads: ResourceThreads, ) -> CanvasPaintThread<'a> { CanvasPaintThread { canvases: HashMap::new(), next_canvas_id: CanvasId(0), webrender_api, - font_context: Arc::new(FontContext::new(font_cache_thread)), + font_context: Arc::new(FontContext::new(font_cache_thread, resource_threads)), } } @@ -58,6 +60,7 @@ impl<'a> CanvasPaintThread<'a> { pub fn start( webrender_api: Box<dyn WebrenderApi + Send>, font_cache_thread: FontCacheThread, + resource_threads: ResourceThreads, ) -> (Sender<ConstellationCanvasMsg>, IpcSender<CanvasMsg>) { let (ipc_sender, ipc_receiver) = ipc::channel::<CanvasMsg>().unwrap(); let msg_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(ipc_receiver); @@ -65,7 +68,8 @@ impl<'a> CanvasPaintThread<'a> { thread::Builder::new() .name("Canvas".to_owned()) .spawn(move || { - let mut canvas_paint_thread = CanvasPaintThread::new(webrender_api, font_cache_thread); + let mut canvas_paint_thread = CanvasPaintThread::new( + webrender_api, font_cache_thread, resource_threads); loop { select! { recv(msg_receiver) -> msg => { diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml index e9871643431..ca580f4495f 100644 --- a/components/gfx/Cargo.toml +++ b/components/gfx/Cargo.toml @@ -43,6 +43,7 @@ ucd = "0.1.1" unicode-bidi = { workspace = true, features = ["with_serde"] } unicode-properties = { workspace = true } unicode-script = { workspace = true } +url = { workspace = true } webrender_api = { workspace = true } webrender_traits = { workspace = true } xi-unicode = { workspace = true } diff --git a/components/gfx/font.rs b/components/gfx/font.rs index 07db050eff2..97709202a99 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -25,8 +25,8 @@ use style::values::computed::{FontStretch, FontStyle, FontWeight}; use unicode_script::Script; use webrender_api::{FontInstanceFlags, FontInstanceKey}; -use crate::font_cache_thread::FontIdentifier; -use crate::font_context::{FontContext, FontSource}; +use crate::font_cache_thread::{FontIdentifier, FontSource}; +use crate::font_context::FontContext; use crate::font_template::{FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods}; use crate::platform::font::{FontTable, PlatformFont}; pub use crate::platform::font_list::fallback_font_families; diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs index 12f72ab309d..5be2ed7d492 100644 --- a/components/gfx/font_cache_thread.rs +++ b/components/gfx/font_cache_thread.rs @@ -5,34 +5,26 @@ use std::borrow::ToOwned; use std::collections::HashMap; use std::ops::{Deref, RangeInclusive}; -use std::sync::{Arc, Mutex}; -use std::{f32, fmt, mem, thread}; +use std::sync::Arc; +use std::{fmt, thread}; use app_units::Au; use atomic_refcell::AtomicRefCell; -use ipc_channel::ipc::{self, IpcBytesSender, IpcReceiver, IpcSender}; -use log::{debug, trace}; +use ipc_channel::ipc::{self, IpcBytesReceiver, IpcBytesSender, IpcReceiver, IpcSender}; +use log::debug; use malloc_size_of_derive::MallocSizeOf; -use net_traits::request::{Destination, Referrer, RequestBuilder}; -use net_traits::{fetch_async, CoreResourceThread, FetchResponseMsg}; use serde::{Deserialize, Serialize}; use servo_atoms::Atom; use servo_url::ServoUrl; -use style::font_face::{ - FontFaceRuleData, FontFaceSourceFormat, FontFaceSourceFormatKeyword, - FontStyle as FontFaceStyle, Source, -}; -use style::media_queries::Device; -use style::shared_lock::SharedRwLockReadGuard; -use style::stylesheets::{Stylesheet, StylesheetInDocument}; +use style::font_face::{FontFaceRuleData, FontStyle as FontFaceStyle}; use style::values::computed::font::{FixedPoint, FontStyleFixedPoint}; use style::values::computed::{FontStretch, FontWeight}; use style::values::specified::FontStretch as SpecifiedFontStretch; use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey}; use webrender_traits::WebRenderFontApi; -use crate::font::{FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope}; -use crate::font_context::FontSource; +use crate::font::{FontDescriptor, FontFamilyName}; +use crate::font_store::FontStore; use crate::font_template::{ FontTemplate, FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods, }; @@ -41,12 +33,6 @@ use crate::platform::font_list::{ SANS_SERIF_FONT_FAMILY, }; -/// A list of font templates that make up a given font family. -#[derive(Default)] -pub struct FontTemplates { - templates: Vec<FontTemplateRef>, -} - #[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] pub enum FontIdentifier { Local(LocalFontIdentifier), @@ -69,73 +55,12 @@ pub struct SerializedFontTemplate { bytes_receiver: ipc_channel::ipc::IpcBytesReceiver, } -impl FontTemplates { - /// Find a font in this family that matches a given descriptor. - pub fn find_for_descriptor( - &mut self, - descriptor_to_match: &FontDescriptor, - ) -> Vec<FontTemplateRef> { - // TODO(Issue #189): optimize lookup for - // regular/bold/italic/bolditalic with fixed offsets and a - // static decision table for fallback between these values. - let matching_templates: Vec<FontTemplateRef> = self - .templates - .iter() - .filter(|template| template.matches_font_descriptor(descriptor_to_match)) - .cloned() - .collect(); - if !matching_templates.is_empty() { - return matching_templates; - } - - // We didn't find an exact match. Do more expensive fuzzy matching. - // TODO(#190): Do a better job. - let mut best_templates = Vec::new(); - let mut best_distance = f32::MAX; - for template in self.templates.iter() { - let distance = template.descriptor_distance(descriptor_to_match); - if distance < best_distance { - best_templates = vec![template.clone()]; - best_distance = distance - } else if distance == best_distance { - best_templates.push(template.clone()); - } - } - - if !best_templates.is_empty() { - return best_templates; - } - - // If a request is made for a font family that exists, - // pick the first valid font in the family if we failed - // to find an exact match for the descriptor. - if let Some(template) = self.templates.first() { - return vec![template.clone()]; - } - - Vec::new() - } - - pub fn add_template(&mut self, new_template: FontTemplate) { - for existing_template in &self.templates { - let existing_template = existing_template.borrow(); - if *existing_template.identifier() == new_template.identifier && - existing_template.descriptor == new_template.descriptor - { - return; - } - } - self.templates - .push(Arc::new(AtomicRefCell::new(new_template))); - } -} - /// Commands that the FontContext sends to the font cache thread. #[derive(Debug, Deserialize, Serialize)] pub enum Command { GetFontTemplates( - FontDescriptor, - FontFamilyDescriptor, + Option<FontDescriptor>, + FontFamilyName, IpcSender<Vec<SerializedFontTemplate>>, ), GetFontInstance( @@ -144,8 +69,8 @@ pub enum Command { FontInstanceFlags, IpcSender<FontInstanceKey>, ), - AddWebFont(CSSFontFaceDescriptors, Vec<Source>, IpcSender<()>), - AddDownloadedWebFont(CSSFontFaceDescriptors, ServoUrl, Vec<u8>, IpcSender<()>), + GetWebFont(IpcBytesReceiver, u32, IpcSender<FontKey>), + GetWebFontInstance(FontKey, f32, FontInstanceFlags, IpcSender<FontInstanceKey>), Exit(IpcSender<()>), Ping, } @@ -154,12 +79,9 @@ pub enum Command { /// font templates that are currently in use. struct FontCache { port: IpcReceiver<Command>, - channel_to_self: IpcSender<Command>, generic_fonts: HashMap<FontFamilyName, LowercaseString>, font_data: HashMap<FontIdentifier, Arc<Vec<u8>>>, - local_families: HashMap<LowercaseString, FontTemplates>, - web_families: HashMap<LowercaseString, FontTemplates>, - core_resource_thread: CoreResourceThread, + local_families: FontStore, webrender_api: Box<dyn WebRenderFontApi>, webrender_fonts: HashMap<FontIdentifier, FontKey>, font_instances: HashMap<(FontKey, Au), FontInstanceKey>, @@ -198,9 +120,9 @@ impl FontCache { let msg = self.port.recv().unwrap(); match msg { - Command::GetFontTemplates(descriptor_to_match, family_descriptor, result) => { + Command::GetFontTemplates(descriptor_to_match, font_family_name, result) => { let templates = - self.find_font_templates(&descriptor_to_match, &family_descriptor); + self.find_font_templates(descriptor_to_match.as_ref(), &font_family_name); debug!("Found templates for descriptor {descriptor_to_match:?}: "); debug!(" {templates:?}"); @@ -238,18 +160,25 @@ impl FontCache { Command::GetFontInstance(identifier, pt_size, flags, result) => { let _ = result.send(self.get_font_instance(identifier, pt_size, flags)); }, - Command::AddWebFont(css_font_face_descriptors, sources, result) => { - self.handle_add_web_font(css_font_face_descriptors, sources, result); + Command::GetWebFont(bytes_receiver, font_index, result_sender) => { + self.webrender_api.forward_add_font_message( + bytes_receiver, + font_index, + result_sender, + ); }, - Command::AddDownloadedWebFont(css_font_face_descriptors, url, data, result) => { - let family_name = css_font_face_descriptors.family_name.clone(); - let templates = &mut self.web_families.get_mut(&family_name).unwrap(); - if let Ok(template) = - FontTemplate::new_web_font(url, Arc::new(data), css_font_face_descriptors) - { - templates.add_template(template); - } - drop(result.send(())); + Command::GetWebFontInstance( + font_key, + font_size, + font_instance_flags, + result_sender, + ) => { + self.webrender_api.forward_add_font_instance_message( + font_key, + font_size, + font_instance_flags, + result_sender, + ); }, Command::Ping => (), Command::Exit(result) => { @@ -260,126 +189,11 @@ impl FontCache { } } - fn handle_add_web_font( - &mut self, - css_font_face_descriptors: CSSFontFaceDescriptors, - mut sources: Vec<Source>, - sender: IpcSender<()>, - ) { - let family_name = css_font_face_descriptors.family_name.clone(); - let src = if let Some(src) = sources.pop() { - src - } else { - sender.send(()).unwrap(); - return; - }; - - if !self.web_families.contains_key(&family_name) { - let templates = FontTemplates::default(); - self.web_families.insert(family_name.clone(), templates); - } - - match src { - Source::Url(url_source) => { - // 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(url.clone().into(), Referrer::NoReferrer) - .destination(Destination::Font); - - let channel_to_self = self.channel_to_self.clone(); - let bytes = Mutex::new(Vec::new()); - let response_valid = Mutex::new(false); - debug!("Loading @font-face {} from {}", family_name, url); - fetch_async(request, &self.core_resource_thread, move |response| { - match response { - FetchResponseMsg::ProcessRequestBody | - FetchResponseMsg::ProcessRequestEOF => (), - FetchResponseMsg::ProcessResponse(meta_result) => { - trace!( - "@font-face {} metadata ok={:?}", - family_name, - meta_result.is_ok() - ); - *response_valid.lock().unwrap() = meta_result.is_ok(); - }, - FetchResponseMsg::ProcessResponseChunk(new_bytes) => { - trace!("@font-face {} chunk={:?}", family_name, new_bytes); - if *response_valid.lock().unwrap() { - bytes.lock().unwrap().extend(new_bytes) - } - }, - FetchResponseMsg::ProcessResponseEOF(response) => { - trace!("@font-face {} EOF={:?}", family_name, response); - if response.is_err() || !*response_valid.lock().unwrap() { - let msg = Command::AddWebFont( - css_font_face_descriptors.clone(), - sources.clone(), - sender.clone(), - ); - channel_to_self.send(msg).unwrap(); - return; - } - let bytes = mem::take(&mut *bytes.lock().unwrap()); - trace!("@font-face {} data={:?}", family_name, bytes); - let bytes = match fontsan::process(&bytes) { - Ok(san) => san, - Err(_) => { - // FIXME(servo/fontsan#1): get an error message - debug!( - "Sanitiser rejected web font: \ - family={} url={:?}", - family_name, url - ); - let msg = Command::AddWebFont( - css_font_face_descriptors.clone(), - sources.clone(), - sender.clone(), - ); - channel_to_self.send(msg).unwrap(); - return; - }, - }; - let command = Command::AddDownloadedWebFont( - css_font_face_descriptors.clone(), - url.clone().into(), - bytes, - sender.clone(), - ); - channel_to_self.send(command).unwrap(); - }, - } - }); - }, - Source::Local(ref font) => { - let font_face_name = LowercaseString::new(&font.name); - let templates = &mut self.web_families.get_mut(&family_name).unwrap(); - let mut found = false; - for_each_variation(&font_face_name, |font_template| { - found = true; - templates.add_template(font_template); - }); - if found { - sender.send(()).unwrap(); - } else { - let msg = - Command::AddWebFont(css_font_face_descriptors.clone(), sources, sender); - self.channel_to_self.send(msg).unwrap(); - } - }, - } - } - fn refresh_local_families(&mut self) { self.local_families.clear(); for_each_available_family(|family_name| { let family_name = LowercaseString::new(&family_name); - self.local_families.entry(family_name).or_default(); + self.local_families.families.entry(family_name).or_default(); }); } @@ -390,9 +204,9 @@ impl FontCache { } } - fn find_templates_in_local_family( + fn find_font_templates( &mut self, - descriptor_to_match: &FontDescriptor, + descriptor_to_match: Option<&FontDescriptor>, family_name: &FontFamilyName, ) -> Vec<FontTemplateRef> { // TODO(Issue #188): look up localized font family names if canonical name not found @@ -401,6 +215,7 @@ impl FontCache { // if such family exists, try to match style to a font let family_name = self.transform_family(family_name); self.local_families + .families .get_mut(&family_name) .map(|font_templates| { if font_templates.templates.is_empty() { @@ -414,41 +229,13 @@ impl FontCache { .unwrap_or_default() } - fn find_templates_in_web_family( - &mut self, - descriptor_to_match: &FontDescriptor, - family_name: &FontFamilyName, - ) -> Vec<FontTemplateRef> { - let family_name = LowercaseString::from(family_name); - self.web_families - .get_mut(&family_name) - .map(|templates| templates.find_for_descriptor(descriptor_to_match)) - .unwrap_or_default() - } - - fn find_font_templates( - &mut self, - descriptor_to_match: &FontDescriptor, - family_descriptor: &FontFamilyDescriptor, - ) -> Vec<FontTemplateRef> { - if family_descriptor.scope == FontSearchScope::Any { - let templates = - self.find_templates_in_web_family(descriptor_to_match, &family_descriptor.name); - if !templates.is_empty() { - return templates; - } - } - - self.find_templates_in_local_family(descriptor_to_match, &family_descriptor.name) - } - fn get_font_instance( &mut self, identifier: FontIdentifier, pt_size: Au, flags: FontInstanceFlags, ) -> FontInstanceKey { - let webrender_api = &self.webrender_api; + let webrender_font_api = &self.webrender_api; let webrender_fonts = &mut self.webrender_fonts; let font_data = self .font_data @@ -466,22 +253,43 @@ impl FontCache { // this for those platforms. #[cfg(target_os = "macos")] if let FontIdentifier::Local(local_font_identifier) = identifier { - return webrender_api + return webrender_font_api .add_system_font(local_font_identifier.native_font_handle()); } - webrender_api.add_font(font_data, identifier.index()) + webrender_font_api.add_font(font_data, identifier.index()) }); *self .font_instances .entry((font_key, pt_size)) .or_insert_with(|| { - webrender_api.add_font_instance(font_key, pt_size.to_f32_px(), flags) + webrender_font_api.add_font_instance(font_key, pt_size.to_f32_px(), flags) }) } } +pub trait FontSource: Clone { + fn find_matching_font_templates( + &self, + descriptor_to_match: Option<&FontDescriptor>, + font_family_name: &FontFamilyName, + ) -> Vec<FontTemplateRef>; + fn get_system_font_instance( + &self, + font_identifier: FontIdentifier, + size: Au, + flags: FontInstanceFlags, + ) -> FontInstanceKey; + fn get_web_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey; + fn get_web_font_instance( + &self, + font_key: FontKey, + size: f32, + flags: FontInstanceFlags, + ) -> FontInstanceKey; +} + /// The public interface to the font cache thread, used by per-thread `FontContext` instances (via /// the `FontSource` trait), and also by layout. #[derive(Clone, Debug, Deserialize, Serialize)] @@ -575,13 +383,9 @@ impl From<&FontFaceRuleData> for CSSFontFaceDescriptors { } impl FontCacheThread { - pub fn new( - core_resource_thread: CoreResourceThread, - webrender_api: Box<dyn WebRenderFontApi + Send>, - ) -> FontCacheThread { + pub fn new(webrender_api: Box<dyn WebRenderFontApi + Send>) -> FontCacheThread { let (chan, port) = ipc::channel().unwrap(); - let channel_to_self = chan.clone(); thread::Builder::new() .name("FontCache".to_owned()) .spawn(move || { @@ -591,12 +395,9 @@ impl FontCacheThread { #[allow(clippy::default_constructed_unit_structs)] let mut cache = FontCache { port, - channel_to_self, generic_fonts, font_data: HashMap::new(), - local_families: HashMap::new(), - web_families: HashMap::new(), - core_resource_thread, + local_families: Default::default(), webrender_api, webrender_fonts: HashMap::new(), font_instances: HashMap::new(), @@ -610,55 +411,6 @@ impl FontCacheThread { FontCacheThread { chan } } - pub fn add_all_web_fonts_from_stylesheet( - &self, - stylesheet: &Stylesheet, - guard: &SharedRwLockReadGuard, - device: &Device, - font_cache_sender: &IpcSender<()>, - synchronous: bool, - ) -> usize { - let (sender, receiver) = if synchronous { - let (sender, receiver) = ipc::channel().unwrap(); - (Some(sender), Some(receiver)) - } else { - (None, None) - }; - - let mut number_loading = 0; - stylesheet.effective_font_face_rules(device, guard, |rule| { - let font_face = match rule.font_face() { - Some(font_face) => font_face, - None => return, - }; - - let sources: Vec<Source> = font_face - .sources() - .0 - .iter() - .rev() - .filter(is_supported_web_font_source) - .cloned() - .collect(); - if sources.is_empty() { - return; - } - - let sender = sender.as_ref().unwrap_or(font_cache_sender).clone(); - self.chan - .send(Command::AddWebFont(rule.into(), sources, sender)) - .unwrap(); - - // Either increment the count of loading web fonts, or wait for a synchronous load. - if let Some(ref receiver) = receiver { - receiver.recv().unwrap(); - } - number_loading += 1; - }); - - number_loading - } - pub fn exit(&self) { let (response_chan, response_port) = ipc::channel().unwrap(); self.chan @@ -671,8 +423,8 @@ impl FontCacheThread { } impl FontSource for FontCacheThread { - fn get_font_instance( - &mut self, + fn get_system_font_instance( + &self, identifier: FontIdentifier, size: Au, flags: FontInstanceFlags, @@ -700,15 +452,15 @@ impl FontSource for FontCacheThread { } fn find_matching_font_templates( - &mut self, - descriptor_to_match: &FontDescriptor, - family_descriptor: FontFamilyDescriptor, + &self, + descriptor_to_match: Option<&FontDescriptor>, + font_family_name: &FontFamilyName, ) -> Vec<FontTemplateRef> { let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); self.chan .send(Command::GetFontTemplates( - descriptor_to_match.clone(), - family_descriptor, + descriptor_to_match.cloned(), + font_family_name.clone(), response_chan, )) .expect("failed to send message to font cache thread"); @@ -736,6 +488,35 @@ impl FontSource for FontCacheThread { }) .collect() } + + fn get_web_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey { + let (result_sender, result_receiver) = + ipc::channel().expect("failed to create IPC channel"); + let (bytes_sender, bytes_receiver) = + ipc::bytes_channel().expect("failed to create IPC channel"); + let _ = self + .chan + .send(Command::GetWebFont(bytes_receiver, index, result_sender)); + let _ = bytes_sender.send(&data); + result_receiver.recv().unwrap() + } + + fn get_web_font_instance( + &self, + font_key: FontKey, + font_size: f32, + font_flags: FontInstanceFlags, + ) -> FontInstanceKey { + let (result_sender, result_receiver) = + ipc::channel().expect("failed to create IPC channel"); + let _ = self.chan.send(Command::GetWebFontInstance( + font_key, + font_size, + font_flags, + result_sender, + )); + result_receiver.recv().unwrap() + } } #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -771,35 +552,3 @@ impl fmt::Display for LowercaseString { self.inner.fmt(f) } } - -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 -} diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index 51bd239b8f2..328e54e066c 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -9,112 +9,72 @@ use std::sync::Arc; use app_units::Au; use fnv::FnvHasher; -use log::debug; +use ipc_channel::ipc::{self, IpcSender}; +use log::{debug, trace}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of_derive::MallocSizeOf; -use parking_lot::{Mutex, RwLock}; +use net_traits::request::{Destination, Referrer, RequestBuilder}; +use net_traits::{fetch_async, CoreResourceThread, FetchResponseMsg, ResourceThreads}; +use parking_lot::{Mutex, ReentrantMutex, RwLock}; use servo_arc::Arc as ServoArc; use style::computed_values::font_variant_caps::T as FontVariantCaps; +use style::font_face::{FontFaceSourceFormat, FontFaceSourceFormatKeyword, Source, UrlSource}; +use style::media_queries::Device; use style::properties::style_structs::Font as FontStyleStruct; -use webrender_api::{FontInstanceFlags, FontInstanceKey}; - -use crate::font::{Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef}; -use crate::font_cache_thread::FontIdentifier; -use crate::font_template::{FontTemplateRef, FontTemplateRefMethods}; +use style::shared_lock::SharedRwLockReadGuard; +use style::stylesheets::{Stylesheet, StylesheetInDocument}; +use style::Atom; +use url::Url; + +use crate::font::{ + Font, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontGroup, FontRef, FontSearchScope, +}; +use crate::font_cache_thread::{ + CSSFontFaceDescriptors, FontIdentifier, FontSource, LowercaseString, +}; +use crate::font_store::{CrossThreadFontStore, CrossThreadWebRenderFontStore}; +use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods}; #[cfg(target_os = "macos")] use crate::platform::core_text_font_cache::CoreTextFontCache; static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h) -pub trait FontSource { - fn get_font_instance( - &mut self, - font_identifier: FontIdentifier, - size: Au, - flags: FontInstanceFlags, - ) -> FontInstanceKey; - fn find_matching_font_templates( - &mut self, - descriptor_to_match: &FontDescriptor, - family_descriptor: FontFamilyDescriptor, - ) -> Vec<FontTemplateRef>; -} - /// 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 font cache thread where /// required. -#[derive(Debug)] pub struct FontContext<S: FontSource> { - font_source: Mutex<S>, - - // TODO: The font context holds a strong ref to the cached fonts - // so they will never be released. Find out a good time to drop them. - // See bug https://github.com/servo/servo/issues/3300 - font_cache: RwLock<HashMap<FontCacheKey, Option<FontRef>>>, - font_template_cache: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>, - font_group_cache: - RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>, + font_source: ReentrantMutex<S>, + resource_threads: ReentrantMutex<CoreResourceThread>, + cache: CachingFontSource<S>, + web_fonts: CrossThreadFontStore, + webrender_font_store: CrossThreadWebRenderFontStore, } impl<S: FontSource> MallocSizeOf for FontContext<S> { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let font_cache_size = self - .font_cache - .read() - .iter() - .map(|(key, font)| { - key.size_of(ops) + font.as_ref().map_or(0, |font| (*font).size_of(ops)) - }) - .sum::<usize>(); - let font_template_cache_size = self - .font_template_cache - .read() - .iter() - .map(|(key, templates)| { - let templates_size = templates - .iter() - .map(|template| template.borrow().size_of(ops)) - .sum::<usize>(); - key.size_of(ops) + templates_size - }) - .sum::<usize>(); - let font_group_cache_size = self - .font_group_cache - .read() - .iter() - .map(|(key, font_group)| key.size_of(ops) + (*font_group.read()).size_of(ops)) - .sum::<usize>(); - - font_cache_size + font_template_cache_size + font_group_cache_size + self.cache.size_of(ops) } } impl<S: FontSource> FontContext<S> { - pub fn new(font_source: S) -> FontContext<S> { + pub fn new(font_source: S, resource_threads: ResourceThreads) -> FontContext<S> { #[allow(clippy::default_constructed_unit_structs)] FontContext { - font_source: Mutex::new(font_source), - font_cache: RwLock::default(), - font_template_cache: RwLock::default(), - font_group_cache: RwLock::default(), + font_source: ReentrantMutex::new(font_source.clone()), + resource_threads: ReentrantMutex::new(resource_threads.core_thread), + cache: CachingFontSource::new(font_source), + web_fonts: Arc::new(RwLock::default()), + webrender_font_store: Arc::new(RwLock::default()), } } /// Invalidate all caches that this [`FontContext`] holds and any in-process platform-specific /// caches. - /// - /// # Safety - /// - /// This should never be called when more than one thread is using the [`FontContext`] or it - /// may leave the context in an inconsistent state. pub fn invalidate_caches(&self) { #[cfg(target_os = "macos")] CoreTextFontCache::clear_core_text_font_cache(); - - self.font_cache.write().clear(); - self.font_template_cache.write().clear(); - self.font_group_cache.write().clear(); + self.cache.invalidate() } /// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`. @@ -132,17 +92,7 @@ impl<S: FontSource> FontContext<S> { style: ServoArc<FontStyleStruct>, size: Au, ) -> Arc<RwLock<FontGroup>> { - let cache_key = FontGroupCacheKey { size, style }; - - if let Some(font_group) = self.font_group_cache.read().get(&cache_key) { - return font_group.clone(); - } - - let font_group = Arc::new(RwLock::new(FontGroup::new(&cache_key.style))); - self.font_group_cache - .write() - .insert(cache_key, font_group.clone()); - font_group + self.cache.font_group_with_size(style, size) } /// Returns a font matching the parameters. Fonts are cached, so repeated calls will return a @@ -187,7 +137,7 @@ impl<S: FontSource> FontContext<S> { font_descriptor: font_descriptor.clone(), }; - if let Some(font) = self.font_cache.read().get(&cache_key).cloned() { + if let Some(font) = self.cache.fonts.read().get(&cache_key).cloned() { return font; } @@ -203,7 +153,7 @@ impl<S: FontSource> FontContext<S> { synthesized_small_caps_font, ) .ok(); - self.font_cache.write().insert(cache_key, font.clone()); + self.cache.fonts.write().insert(cache_key, font.clone()); font } @@ -212,30 +162,23 @@ impl<S: FontSource> FontContext<S> { descriptor_to_match: &FontDescriptor, family_descriptor: &FontFamilyDescriptor, ) -> Vec<FontTemplateRef> { - let cache_key = FontTemplateCacheKey { - font_descriptor: descriptor_to_match.clone(), - family_descriptor: family_descriptor.clone(), - }; - - if let Some(templates) = self.font_template_cache.read().get(&cache_key).cloned() { - return templates; + // First try to find an appropriate web font that matches this descriptor. + if family_descriptor.scope == FontSearchScope::Any { + let family_name = LowercaseString::from(&family_descriptor.name); + if let Some(templates) = self + .web_fonts + .read() + .families + .get(&family_name) + .map(|templates| templates.find_for_descriptor(Some(descriptor_to_match))) + { + return templates; + } } - debug!( - "FontContext::font_template cache miss for template_descriptor={:?} family_descriptor={:?}", - descriptor_to_match, - family_descriptor - ); - - let templates = self - .font_source - .lock() - .find_matching_font_templates(descriptor_to_match, family_descriptor.clone()); - - self.font_template_cache - .write() - .insert(cache_key, templates.clone()); - templates + // If not request a matching font from the system font cache. + self.cache + .matching_templates(descriptor_to_match, family_descriptor) } /// Create a `Font` for use in layout calculations, from a `FontTemplateData` returned by the @@ -251,16 +194,413 @@ impl<S: FontSource> FontContext<S> { font_descriptor.clone(), synthesized_small_caps, )?; - font.font_key = self.font_source.lock().get_font_instance( - font_template.identifier(), - font_descriptor.pt_size, - font.webrender_font_instance_flags(), - ); + + let font_source = self.font_source.lock(); + font.font_key = match font_template.identifier() { + FontIdentifier::Local(_) => font_source.get_system_font_instance( + font_template.identifier(), + font_descriptor.pt_size, + font.webrender_font_instance_flags(), + ), + FontIdentifier::Web(_) => self.webrender_font_store.write().get_font_instance( + &*font_source, + font_template.clone(), + font_descriptor.pt_size, + font.webrender_font_instance_flags(), + ), + }; Ok(Arc::new(font)) } } +#[derive(Clone)] +pub struct WebFontDownloadState { + css_font_face_descriptors: Arc<CSSFontFaceDescriptors>, + remaining_sources: Vec<Source>, + result_sender: IpcSender<()>, + core_resource_thread: CoreResourceThread, + local_fonts: Arc<HashMap<Atom, Option<FontTemplateRef>>>, +} + +pub trait FontContextWebFontMethods { + fn add_all_web_fonts_from_stylesheet( + &self, + stylesheet: &Stylesheet, + guard: &SharedRwLockReadGuard, + device: &Device, + font_cache_sender: &IpcSender<()>, + synchronous: bool, + ) -> usize; + fn process_next_web_font_source(&self, web_font_download_state: WebFontDownloadState); +} + +impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontContext<S>> { + fn add_all_web_fonts_from_stylesheet( + &self, + stylesheet: &Stylesheet, + guard: &SharedRwLockReadGuard, + device: &Device, + font_cache_sender: &IpcSender<()>, + synchronous: bool, + ) -> usize { + let (result_sender, receiver) = if synchronous { + let (sender, receiver) = ipc::channel().unwrap(); + (Some(sender), Some(receiver)) + } else { + (None, None) + }; + + let mut number_loading = 0; + stylesheet.effective_font_face_rules(device, guard, |rule| { + let font_face = match rule.font_face() { + Some(font_face) => font_face, + None => return, + }; + + let sources: Vec<Source> = font_face + .sources() + .0 + .iter() + .rev() + .filter(is_supported_web_font_source) + .cloned() + .collect(); + if sources.is_empty() { + return; + } + + // 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_name = FontFamilyName::Specific(family_name.name.clone()); + self.font_source + .lock() + .find_matching_font_templates(None, &family_name) + .first() + .cloned() + }); + } + } + + let result_sender = result_sender.as_ref().unwrap_or(font_cache_sender).clone(); + self.process_next_web_font_source(WebFontDownloadState { + css_font_face_descriptors: Arc::new(rule.into()), + remaining_sources: sources, + result_sender, + core_resource_thread: self.resource_threads.lock().clone(), + local_fonts: Arc::new(local_fonts), + }); + + // Either increment the count of loading web fonts, or wait for a synchronous load. + if let Some(ref receiver) = receiver { + receiver.recv().unwrap(); + } + number_loading += 1; + }); + + number_loading + } + + fn process_next_web_font_source(&self, mut state: WebFontDownloadState) { + let Some(source) = state.remaining_sources.pop() else { + state.result_sender.send(()).unwrap(); + 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| { + FontTemplate::new_for_local_web_font( + local_template, + &state.css_font_face_descriptors, + ) + .ok() + }) + { + this.web_fonts + .write() + .families + .entry(web_font_family_name.clone()) + .or_default() + .add_template(new_template); + drop(state.result_sender.send(())); + } else { + this.process_next_web_font_source(state); + } + }, + } + } +} + +struct RemoteWebFontDownloader<FCT: FontSource> { + font_context: Arc<FontContext<FCT>>, + url: ServoArc<Url>, + web_font_family_name: LowercaseString, + response_valid: Mutex<bool>, + response_data: Mutex<Vec<u8>>, +} + +enum DownloaderResponseResult { + InProcess, + Finished, + Failure, +} + +impl<FCT: FontSource + Send + 'static> RemoteWebFontDownloader<FCT> { + fn download( + url_source: UrlSource, + font_context: Arc<FontContext<FCT>>, + web_font_family_name: LowercaseString, + 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(url.clone().into(), Referrer::NoReferrer) + .destination(Destination::Font); + + debug!("Loading @font-face {} from {}", web_font_family_name, url); + let downloader = Self { + font_context, + url, + web_font_family_name, + response_valid: Mutex::new(false), + response_data: Mutex::default(), + }; + + let core_resource_thread_clone = state.core_resource_thread.clone(); + fetch_async( + request, + &core_resource_thread_clone, + move |response_message| match downloader.handle_web_font_fetch_message(response_message) + { + DownloaderResponseResult::InProcess => {}, + DownloaderResponseResult::Finished => { + if !downloader.process_downloaded_font_and_signal_completion(&state) { + downloader + .font_context + .process_next_web_font_source(state.clone()) + } + }, + DownloaderResponseResult::Failure => downloader + .font_context + .process_next_web_font_source(state.clone()), + }, + ) + } + + /// 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(&self, state: &WebFontDownloadState) -> bool { + let font_data = std::mem::take(&mut *self.response_data.lock()); + trace!( + "@font-face {} data={:?}", + self.web_font_family_name, + font_data + ); + + let font_data = match fontsan::process(&font_data) { + Ok(bytes) => bytes, + Err(error) => { + debug!( + "Sanitiser rejected web font: family={} url={:?} with {error:?}", + self.web_font_family_name, self.url, + ); + return false; + }, + }; + + let Ok(new_template) = FontTemplate::new_for_remote_web_font( + self.url.clone().into(), + Arc::new(font_data), + &state.css_font_face_descriptors, + ) else { + return false; + }; + + let family_name = state.css_font_face_descriptors.family_name.clone(); + self.font_context + .web_fonts + .write() + .families + .entry(family_name.clone()) + .or_default() + .add_template(new_template); + + // Signal the Document that we have finished trying to load this web font. + drop(state.result_sender.send(())); + true + } + + fn handle_web_font_fetch_message( + &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.lock() = 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.lock() { + self.response_data.lock().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.lock() { + return DownloaderResponseResult::Failure; + } + DownloaderResponseResult::Finished + }, + } + } +} + +#[derive(Default)] +pub struct CachingFontSource<FCT: FontSource> { + font_cache_thread: ReentrantMutex<FCT>, + fonts: RwLock<HashMap<FontCacheKey, Option<FontRef>>>, + templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>, + resolved_font_groups: + RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>, +} + +impl<FCT: FontSource> CachingFontSource<FCT> { + fn new(font_cache_thread: FCT) -> Self { + Self { + font_cache_thread: ReentrantMutex::new(font_cache_thread), + fonts: Default::default(), + templates: Default::default(), + resolved_font_groups: Default::default(), + } + } + + fn invalidate(&self) { + self.fonts.write().clear(); + self.templates.write().clear(); + self.resolved_font_groups.write().clear(); + } + + pub fn matching_templates( + &self, + descriptor_to_match: &FontDescriptor, + family_descriptor: &FontFamilyDescriptor, + ) -> Vec<FontTemplateRef> { + let cache_key = FontTemplateCacheKey { + font_descriptor: descriptor_to_match.clone(), + family_descriptor: family_descriptor.clone(), + }; + if let Some(templates) = self.templates.read().get(&cache_key).cloned() { + return templates; + } + + debug!( + "CachingFontSource: cache miss for template_descriptor={:?} family_descriptor={:?}", + descriptor_to_match, family_descriptor + ); + let templates = self + .font_cache_thread + .lock() + .find_matching_font_templates(Some(descriptor_to_match), &family_descriptor.name); + self.templates.write().insert(cache_key, templates.clone()); + + templates + } + + pub fn font_group_with_size( + &self, + style: ServoArc<FontStyleStruct>, + size: Au, + ) -> Arc<RwLock<FontGroup>> { + let cache_key = FontGroupCacheKey { size, style }; + if let Some(font_group) = self.resolved_font_groups.read().get(&cache_key) { + return font_group.clone(); + } + let font_group = Arc::new(RwLock::new(FontGroup::new(&cache_key.style))); + self.resolved_font_groups + .write() + .insert(cache_key, font_group.clone()); + font_group + } +} + +impl<FCT: FontSource> MallocSizeOf for CachingFontSource<FCT> { + 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::<usize>(); + let font_template_cache_size = self + .templates + .read() + .iter() + .map(|(key, templates)| { + let templates_size = templates + .iter() + .map(|template| template.borrow().size_of(ops)) + .sum::<usize>(); + key.size_of(ops) + templates_size + }) + .sum::<usize>(); + 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::<usize>(); + + font_cache_size + font_template_cache_size + font_group_cache_size + } +} + #[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)] struct FontCacheKey { font_identifier: FontIdentifier, @@ -296,3 +636,35 @@ impl Hash for FontGroupCacheKey { self.style.hash.hash(hasher) } } + +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 +} diff --git a/components/gfx/font_store.rs b/components/gfx/font_store.rs new file mode 100644 index 00000000000..888a0ae0a1e --- /dev/null +++ b/components/gfx/font_store.rs @@ -0,0 +1,130 @@ +/* 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; +use std::sync::Arc; + +use app_units::Au; +use atomic_refcell::AtomicRefCell; +use parking_lot::RwLock; +use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey}; + +use crate::font::FontDescriptor; +use crate::font_cache_thread::{FontIdentifier, FontSource, LowercaseString}; +use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods}; + +#[derive(Default)] +pub struct FontStore { + pub(crate) families: HashMap<LowercaseString, FontTemplates>, +} +pub(crate) type CrossThreadFontStore = Arc<RwLock<FontStore>>; + +impl FontStore { + pub(crate) fn clear(&mut self) { + self.families.clear(); + } +} + +#[derive(Default)] +pub struct WebRenderFontStore { + pub(crate) webrender_font_key_map: HashMap<FontIdentifier, FontKey>, + pub(crate) webrender_font_instance_map: HashMap<(FontKey, Au), FontInstanceKey>, +} +pub(crate) type CrossThreadWebRenderFontStore = Arc<RwLock<WebRenderFontStore>>; + +impl WebRenderFontStore { + pub(crate) fn get_font_instance<FCT: FontSource>( + &mut self, + font_cache_thread: &FCT, + font_template: FontTemplateRef, + pt_size: Au, + flags: FontInstanceFlags, + ) -> FontInstanceKey { + let webrender_font_key_map = &mut self.webrender_font_key_map; + let identifier = font_template.identifier().clone(); + let font_key = *webrender_font_key_map + .entry(identifier.clone()) + .or_insert_with(|| { + font_cache_thread.get_web_font(font_template.data(), identifier.index()) + }); + + *self + .webrender_font_instance_map + .entry((font_key, pt_size)) + .or_insert_with(|| { + font_cache_thread.get_web_font_instance(font_key, pt_size.to_f32_px(), flags) + }) + } +} + +/// A list of font templates that make up a given font family. +#[derive(Clone, Debug, Default)] +pub struct FontTemplates { + pub(crate) templates: Vec<FontTemplateRef>, +} + +impl FontTemplates { + /// Find a font in this family that matches a given descriptor. + pub fn find_for_descriptor( + &self, + descriptor_to_match: Option<&FontDescriptor>, + ) -> Vec<FontTemplateRef> { + let Some(descriptor_to_match) = descriptor_to_match else { + return self.templates.clone(); + }; + + // TODO(Issue #189): optimize lookup for + // regular/bold/italic/bolditalic with fixed offsets and a + // static decision table for fallback between these values. + let matching_templates: Vec<FontTemplateRef> = self + .templates + .iter() + .filter(|template| template.matches_font_descriptor(descriptor_to_match)) + .cloned() + .collect(); + if !matching_templates.is_empty() { + return matching_templates; + } + + // We didn't find an exact match. Do more expensive fuzzy matching. + // TODO(#190): Do a better job. + let mut best_templates = Vec::new(); + let mut best_distance = f32::MAX; + for template in self.templates.iter() { + let distance = template.descriptor_distance(descriptor_to_match); + if distance < best_distance { + best_templates = vec![template.clone()]; + best_distance = distance + } else if distance == best_distance { + best_templates.push(template.clone()); + } + } + + if !best_templates.is_empty() { + return best_templates; + } + + // If a request is made for a font family that exists, + // pick the first valid font in the family if we failed + // to find an exact match for the descriptor. + if let Some(template) = self.templates.first() { + return vec![template.clone()]; + } + + Vec::new() + } + + pub fn add_template(&mut self, new_template: FontTemplate) { + for existing_template in &self.templates { + let existing_template = existing_template.borrow(); + if *existing_template.identifier() == new_template.identifier && + existing_template.descriptor == new_template.descriptor + { + return; + } + } + self.templates + .push(Arc::new(AtomicRefCell::new(new_template))); + } +} diff --git a/components/gfx/font_template.rs b/components/gfx/font_template.rs index 67f98b17c98..3bc9f21acf0 100644 --- a/components/gfx/font_template.rs +++ b/components/gfx/font_template.rs @@ -100,7 +100,7 @@ impl FontTemplateDescriptor { fn override_values_with_css_font_template_descriptors( &mut self, - css_font_template_descriptors: CSSFontFaceDescriptors, + css_font_template_descriptors: &CSSFontFaceDescriptors, ) { if let Some(weight) = css_font_template_descriptors.weight { self.weight = weight; @@ -117,8 +117,8 @@ impl FontTemplateDescriptor { if let Some(stretch) = css_font_template_descriptors.stretch { self.stretch = stretch; } - if let Some(unicode_range) = css_font_template_descriptors.unicode_range { - self.unicode_range = Some(unicode_range); + if let Some(ref unicode_range) = css_font_template_descriptors.unicode_range { + self.unicode_range = Some(unicode_range.clone()); } } } @@ -126,6 +126,7 @@ impl FontTemplateDescriptor { /// This describes all the information needed to create /// font instance handles. It contains a unique /// FontTemplateData structure that is platform specific. +#[derive(Clone)] pub struct FontTemplate { pub identifier: FontIdentifier, pub descriptor: FontTemplateDescriptor, @@ -154,7 +155,8 @@ impl Debug for FontTemplate { /// is common, regardless of the number of instances of /// this font handle per thread. impl FontTemplate { - pub fn new_local( + /// Create a new [`FontTemplate`] for a system font installed locally. + pub fn new_for_local_font( identifier: LocalFontIdentifier, descriptor: FontTemplateDescriptor, ) -> FontTemplate { @@ -165,10 +167,11 @@ impl FontTemplate { } } - pub fn new_web_font( + /// Create a new [`FontTemplate`] for a `@font-family` with a `url(...)` `src` font. + pub fn new_for_remote_web_font( url: ServoUrl, data: Arc<Vec<u8>>, - css_font_template_descriptors: CSSFontFaceDescriptors, + css_font_template_descriptors: &CSSFontFaceDescriptors, ) -> Result<FontTemplate, &'static str> { let identifier = FontIdentifier::Web(url.clone()); let Ok(handle) = PlatformFont::new_from_data(identifier, data.clone(), 0, None) else { @@ -185,6 +188,20 @@ impl FontTemplate { }) } + /// Create a new [`FontTemplate`] for a `@font-family` with a `local(...)` `src`. This takes in + /// the template of the local font and creates a new one that reflects the properties specified + /// by `@font-family` in the stylesheet. + pub fn new_for_local_web_font( + local_template: FontTemplateRef, + css_font_template_descriptors: &CSSFontFaceDescriptors, + ) -> Result<FontTemplate, &'static str> { + let mut alias_template = local_template.borrow().clone(); + alias_template + .descriptor + .override_values_with_css_font_template_descriptors(css_font_template_descriptors); + Ok(alias_template) + } + pub fn identifier(&self) -> &FontIdentifier { &self.identifier } @@ -233,6 +250,10 @@ impl FontTemplateRefMethods for FontTemplateRef { } fn data(&self) -> Arc<Vec<u8>> { + if let Some(data) = self.borrow().data.clone() { + return data; + } + let mut template = self.borrow_mut(); let identifier = template.identifier.clone(); template diff --git a/components/gfx/lib.rs b/components/gfx/lib.rs index 4840918fe1f..694cbfc006a 100644 --- a/components/gfx/lib.rs +++ b/components/gfx/lib.rs @@ -7,6 +7,7 @@ pub mod font; pub mod font_cache_thread; pub mod font_context; +pub mod font_store; pub mod font_template; #[allow(unsafe_code)] pub mod platform; diff --git a/components/gfx/platform/freetype/android/font_list.rs b/components/gfx/platform/freetype/android/font_list.rs index 6e6b45c5ad7..4f24d677889 100644 --- a/components/gfx/platform/freetype/android/font_list.rs +++ b/components/gfx/platform/freetype/android/font_list.rs @@ -486,7 +486,10 @@ where None => StyleFontStyle::NORMAL, }; let descriptor = FontTemplateDescriptor::new(weight, stretch, style); - callback(FontTemplate::new_local(local_font_identifier, descriptor)); + callback(FontTemplate::new_for_local_font( + local_font_identifier, + descriptor, + )); }; if let Some(family) = FONT_LIST.find_family(family_name) { diff --git a/components/gfx/platform/freetype/font_list.rs b/components/gfx/platform/freetype/font_list.rs index 00db8e6855e..78ea0a2ad50 100644 --- a/components/gfx/platform/freetype/font_list.rs +++ b/components/gfx/platform/freetype/font_list.rs @@ -152,7 +152,10 @@ where }; let descriptor = FontTemplateDescriptor::new(weight, stretch, style); - callback(FontTemplate::new_local(local_font_identifier, descriptor)) + callback(FontTemplate::new_for_local_font( + local_font_identifier, + descriptor, + )) } FcFontSetDestroy(matches); diff --git a/components/gfx/platform/freetype/ohos/font_list.rs b/components/gfx/platform/freetype/ohos/font_list.rs index 95d7209c6ad..a9a21d63b7b 100644 --- a/components/gfx/platform/freetype/ohos/font_list.rs +++ b/components/gfx/platform/freetype/ohos/font_list.rs @@ -158,7 +158,10 @@ where stretch, style, }; - callback(FontTemplate::new_local(local_font_identifier, descriptor)); + callback(FontTemplate::new_for_local_font( + local_font_identifier, + descriptor, + )); }; if let Some(family) = FONT_LIST.find_family(family_name) { diff --git a/components/gfx/platform/macos/font_list.rs b/components/gfx/platform/macos/font_list.rs index 272916e2d07..faeb54af0d4 100644 --- a/components/gfx/platform/macos/font_list.rs +++ b/components/gfx/platform/macos/font_list.rs @@ -81,7 +81,7 @@ where postscript_name: Atom::from(family_descriptor.font_name()), path: Atom::from(path), }; - callback(FontTemplate::new_local(identifier, descriptor)); + callback(FontTemplate::new_for_local_font(identifier, descriptor)); } } } diff --git a/components/gfx/platform/windows/font_list.rs b/components/gfx/platform/windows/font_list.rs index 9cfdbfd988e..5d1f076d6f9 100644 --- a/components/gfx/platform/windows/font_list.rs +++ b/components/gfx/platform/windows/font_list.rs @@ -81,7 +81,7 @@ where let local_font_identifier = LocalFontIdentifier { font_descriptor: Arc::new(font.to_descriptor()), }; - callback(FontTemplate::new_local( + callback(FontTemplate::new_for_local_font( local_font_identifier, template_descriptor, )) diff --git a/components/gfx/tests/font_context.rs b/components/gfx/tests/font_context.rs index 54babf3ef83..06870f6f88c 100644 --- a/components/gfx/tests/font_context.rs +++ b/components/gfx/tests/font_context.rs @@ -2,7 +2,7 @@ * 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::cell::Cell; +use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::fs::File; use std::io::prelude::*; @@ -13,9 +13,12 @@ use app_units::Au; use gfx::font::{ fallback_font_families, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope, }; -use gfx::font_cache_thread::{CSSFontFaceDescriptors, FontIdentifier, FontTemplates}; -use gfx::font_context::{FontContext, FontSource}; +use gfx::font_cache_thread::{CSSFontFaceDescriptors, FontIdentifier, FontSource}; +use gfx::font_context::FontContext; +use gfx::font_store::FontTemplates; use gfx::font_template::{FontTemplate, FontTemplateRef}; +use ipc_channel::ipc; +use net_traits::ResourceThreads; use servo_arc::Arc; use servo_atoms::Atom; use servo_url::ServoUrl; @@ -27,15 +30,16 @@ use style::values::computed::font::{ }; use style::values::computed::{FontLanguageOverride, XLang}; use style::values::generics::font::LineHeight; -use webrender_api::{FontInstanceFlags, FontInstanceKey, IdNamespace}; +use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, IdNamespace}; -struct TestFontSource { - families: HashMap<String, FontTemplates>, +#[derive(Clone)] +struct MockFontCacheThread { + families: RefCell<HashMap<String, FontTemplates>>, find_font_count: Rc<Cell<isize>>, } -impl TestFontSource { - fn new() -> TestFontSource { +impl MockFontCacheThread { + fn new() -> MockFontCacheThread { let mut csstest_ascii = FontTemplates::default(); Self::add_face(&mut csstest_ascii, "csstest-ascii"); @@ -50,8 +54,8 @@ impl TestFontSource { families.insert("CSSTest Basic".to_owned(), csstest_basic); families.insert(fallback_font_families(None)[0].to_owned(), fallback); - TestFontSource { - families, + MockFontCacheThread { + families: RefCell::new(families), find_font_count: Rc::new(Cell::new(0)), } } @@ -77,37 +81,51 @@ impl TestFontSource { let file = File::open(path).unwrap(); let data: Vec<u8> = file.bytes().map(|b| b.unwrap()).collect(); family.add_template( - FontTemplate::new_web_font( + FontTemplate::new_for_remote_web_font( Self::url_for_font_name(name), std::sync::Arc::new(data), - CSSFontFaceDescriptors::new(name), + &CSSFontFaceDescriptors::new(name), ) .unwrap(), ); } } -impl FontSource for TestFontSource { - fn get_font_instance( - &mut self, - _font_identifier: FontIdentifier, - _size: Au, - _flags: FontInstanceFlags, - ) -> FontInstanceKey { - FontInstanceKey(IdNamespace(0), 0) - } - +impl FontSource for MockFontCacheThread { fn find_matching_font_templates( - &mut self, - descriptor_to_match: &FontDescriptor, - family_descriptor: FontFamilyDescriptor, + &self, + descriptor_to_match: Option<&FontDescriptor>, + font_family_name: &FontFamilyName, ) -> Vec<FontTemplateRef> { self.find_font_count.set(self.find_font_count.get() + 1); self.families - .get_mut(family_descriptor.name()) + .borrow_mut() + .get_mut(font_family_name.name()) .map(|family| family.find_for_descriptor(descriptor_to_match)) .unwrap_or_default() } + + fn get_system_font_instance( + &self, + _: FontIdentifier, + _: Au, + _: FontInstanceFlags, + ) -> FontInstanceKey { + FontInstanceKey(IdNamespace(0), 0) + } + + fn get_web_font(&self, _: std::sync::Arc<Vec<u8>>, _: u32) -> webrender_api::FontKey { + FontKey(IdNamespace(0), 0) + } + + fn get_web_font_instance( + &self, + _: webrender_api::FontKey, + _: f32, + _: FontInstanceFlags, + ) -> FontInstanceKey { + FontInstanceKey(IdNamespace(0), 0) + } } fn style() -> FontStyleStruct { @@ -147,10 +165,16 @@ fn font_family(names: Vec<&str>) -> FontFamily { } } +fn mock_resource_threads() -> ResourceThreads { + let (core_sender, _) = ipc::channel().unwrap(); + let (storage_sender, _) = ipc::channel().unwrap(); + ResourceThreads::new(core_sender, storage_sender) +} + #[test] fn test_font_group_is_cached_by_style() { - let source = TestFontSource::new(); - let context = FontContext::new(source); + let source = MockFontCacheThread::new(); + let context = FontContext::new(source, mock_resource_threads()); let style1 = style(); @@ -176,9 +200,9 @@ fn test_font_group_is_cached_by_style() { #[test] fn test_font_group_find_by_codepoint() { - let source = TestFontSource::new(); + let source = MockFontCacheThread::new(); let count = source.find_font_count.clone(); - let mut context = FontContext::new(source); + let mut context = FontContext::new(source, mock_resource_threads()); let mut style = style(); style.set_font_family(font_family(vec!["CSSTest ASCII", "CSSTest Basic"])); @@ -188,7 +212,7 @@ fn test_font_group_find_by_codepoint() { let font = group.write().find_by_codepoint(&mut context, 'a').unwrap(); assert_eq!( font.identifier(), - TestFontSource::identifier_for_font_name("csstest-ascii") + MockFontCacheThread::identifier_for_font_name("csstest-ascii") ); assert_eq!( count.get(), @@ -199,7 +223,7 @@ fn test_font_group_find_by_codepoint() { let font = group.write().find_by_codepoint(&mut context, 'a').unwrap(); assert_eq!( font.identifier(), - TestFontSource::identifier_for_font_name("csstest-ascii") + MockFontCacheThread::identifier_for_font_name("csstest-ascii") ); assert_eq!( count.get(), @@ -210,15 +234,15 @@ fn test_font_group_find_by_codepoint() { let font = group.write().find_by_codepoint(&mut context, 'á').unwrap(); assert_eq!( font.identifier(), - TestFontSource::identifier_for_font_name("csstest-basic-regular") + MockFontCacheThread::identifier_for_font_name("csstest-basic-regular") ); assert_eq!(count.get(), 2, "both fonts should now have been loaded"); } #[test] fn test_font_fallback() { - let source = TestFontSource::new(); - let mut context = FontContext::new(source); + let source = MockFontCacheThread::new(); + let mut context = FontContext::new(source, mock_resource_threads()); let mut style = style(); style.set_font_family(font_family(vec!["CSSTest ASCII"])); @@ -228,23 +252,23 @@ fn test_font_fallback() { let font = group.write().find_by_codepoint(&mut context, 'a').unwrap(); assert_eq!( font.identifier(), - TestFontSource::identifier_for_font_name("csstest-ascii"), + MockFontCacheThread::identifier_for_font_name("csstest-ascii"), "a family in the group should be used if there is a matching glyph" ); let font = group.write().find_by_codepoint(&mut context, 'á').unwrap(); assert_eq!( font.identifier(), - TestFontSource::identifier_for_font_name("csstest-basic-regular"), + MockFontCacheThread::identifier_for_font_name("csstest-basic-regular"), "a fallback font should be used if there is no matching glyph in the group" ); } #[test] fn test_font_template_is_cached() { - let source = TestFontSource::new(); + let source = MockFontCacheThread::new(); let count = source.find_font_count.clone(); - let context = FontContext::new(source); + let context = FontContext::new(source, mock_resource_threads()); let mut font_descriptor = FontDescriptor { weight: FontWeight::normal(), diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 8cb612bd1de..22cbb52fc6a 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -26,7 +26,7 @@ use fnv::FnvHashMap; use fxhash::{FxHashMap, FxHashSet}; use gfx::font; use gfx::font_cache_thread::FontCacheThread; -use gfx::font_context::FontContext; +use gfx::font_context::{FontContext, FontContextWebFontMethods}; use histogram::Histogram; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; @@ -54,6 +54,7 @@ use log::{debug, error, trace, warn}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use metrics::{PaintTimeMetrics, ProfilerMetadataFactory}; use net_traits::image_cache::{ImageCache, UsePlaceholder}; +use net_traits::ResourceThreads; use parking_lot::RwLock; use profile_traits::mem::{Report, ReportKind}; use profile_traits::path; @@ -133,13 +134,10 @@ pub struct LayoutThread { /// Reference to the script thread image cache. image_cache: Arc<dyn ImageCache>, - /// Public interface to the font cache thread. - font_cache_thread: FontCacheThread, - - /// A FontContext to be used during layout. + /// A FontContext tFontCacheThreadImplg layout. font_context: Arc<FontContext<FontCacheThread>>, - /// Is this the first reflow in this LayoutThread? + /// Is this the first reflow iFontCacheThreadImplread? first_reflow: Cell<bool>, /// Flag to indicate whether to use parallel operations @@ -207,6 +205,7 @@ impl LayoutFactory for LayoutFactoryImpl { config.constellation_chan, config.script_chan, config.image_cache, + config.resource_threads, config.font_cache_thread, config.time_profiler_chan, config.webrender_api_sender, @@ -566,6 +565,7 @@ impl LayoutThread { constellation_chan: IpcSender<ConstellationMsg>, script_chan: IpcSender<ConstellationControlMsg>, image_cache: Arc<dyn ImageCache>, + resource_threads: ResourceThreads, font_cache_thread: FontCacheThread, time_profiler_chan: profile_time::ProfilerChan, webrender_api: WebRenderScriptApi, @@ -575,7 +575,7 @@ impl LayoutThread { // Let webrender know about this pipeline by sending an empty display list. webrender_api.send_initial_transaction(id.into()); - let font_context = Arc::new(FontContext::new(font_cache_thread.clone())); + let font_context = Arc::new(FontContext::new(font_cache_thread, resource_threads)); let device = Device::new( MediaType::screen(), QuirksMode::NoQuirks, @@ -604,7 +604,6 @@ impl LayoutThread { time_profiler_chan, registered_painters: RegisteredPaintersImpl(Default::default()), image_cache, - font_cache_thread, font_context, first_reflow: Cell::new(true), font_cache_sender: ipc_font_cache_sender, @@ -711,14 +710,13 @@ impl LayoutThread { // Find all font-face rules and notify the font cache of them. // GWTODO: Need to handle unloading web fonts. if stylesheet.is_effective_for_device(self.stylist.device(), guard) { - let newly_loading_font_count = - self.font_cache_thread.add_all_web_fonts_from_stylesheet( - stylesheet, - guard, - self.stylist.device(), - &self.font_cache_sender, - self.debug.load_webfonts_synchronously, - ); + let newly_loading_font_count = self.font_context.add_all_web_fonts_from_stylesheet( + stylesheet, + guard, + self.stylist.device(), + &self.font_cache_sender, + self.debug.load_webfonts_synchronously, + ); if !self.debug.load_webfonts_synchronously { self.outstanding_web_fonts diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index 4dd245a4925..a482f8592c1 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -10,6 +10,7 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; +use std::fmt::Debug; use std::ops::{Deref, DerefMut}; use std::process; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -24,7 +25,7 @@ use euclid::{Point2D, Scale, Size2D, Vector2D}; use fnv::FnvHashMap; use fxhash::FxHashMap; use gfx::font_cache_thread::FontCacheThread; -use gfx::font_context::FontContext; +use gfx::font_context::{FontContext, FontContextWebFontMethods}; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use layout::context::LayoutContext; @@ -41,6 +42,7 @@ use log::{debug, error, warn}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use metrics::{PaintTimeMetrics, ProfilerMetadataFactory}; use net_traits::image_cache::{ImageCache, UsePlaceholder}; +use net_traits::ResourceThreads; use parking_lot::RwLock; use profile_traits::mem::{Report, ReportKind}; use profile_traits::path; @@ -120,10 +122,6 @@ pub struct LayoutThread { /// Reference to the script thread image cache. image_cache: Arc<dyn ImageCache>, - /// Public interface to the font cache thread. This needs to be behind a [`ReentrantMutex`], - /// because some font cache operations can trigger others. - font_cache_thread: FontCacheThread, - /// A FontContext to be used during layout. font_context: Arc<FontContext<FontCacheThread>>, @@ -183,6 +181,7 @@ impl LayoutFactory for LayoutFactoryImpl { config.constellation_chan, config.script_chan, config.image_cache, + config.resource_threads, config.font_cache_thread, config.time_profiler_chan, config.webrender_api_sender, @@ -483,6 +482,7 @@ impl LayoutThread { constellation_chan: IpcSender<ConstellationMsg>, script_chan: IpcSender<ConstellationControlMsg>, image_cache: Arc<dyn ImageCache>, + resource_threads: ResourceThreads, font_cache_thread: FontCacheThread, time_profiler_chan: profile_time::ProfilerChan, webrender_api_sender: WebRenderScriptApi, @@ -494,7 +494,7 @@ impl LayoutThread { // The device pixel ratio is incorrect (it does not have the hidpi value), // but it will be set correctly when the initial reflow takes place. - let font_context = Arc::new(FontContext::new(font_cache_thread.clone())); + let font_context = Arc::new(FontContext::new(font_cache_thread, resource_threads)); let device = Device::new( MediaType::screen(), QuirksMode::NoQuirks, @@ -523,7 +523,6 @@ impl LayoutThread { time_profiler_chan, registered_painters: RegisteredPaintersImpl(Default::default()), image_cache, - font_cache_thread, font_context, first_reflow: Cell::new(true), font_cache_sender: ipc_font_cache_sender, @@ -627,14 +626,13 @@ impl LayoutThread { // Find all font-face rules and notify the font cache of them. // GWTODO: Need to handle unloading web fonts. if stylesheet.is_effective_for_device(self.stylist.device(), guard) { - let newly_loading_font_count = - self.font_cache_thread.add_all_web_fonts_from_stylesheet( - stylesheet, - guard, - self.stylist.device(), - &self.font_cache_sender, - self.debug.load_webfonts_synchronously, - ); + let newly_loading_font_count = self.font_context.add_all_web_fonts_from_stylesheet( + stylesheet, + guard, + self.stylist.device(), + &self.font_cache_sender, + self.debug.load_webfonts_synchronously, + ); if !self.debug.load_webfonts_synchronously { self.outstanding_web_fonts @@ -1243,7 +1241,6 @@ impl RegisteredSpeculativePainters for RegisteredPaintersImpl { } } -#[derive(Debug)] struct LayoutFontMetricsProvider(Arc<FontContext<FontCacheThread>>); impl FontMetricsProvider for LayoutFontMetricsProvider { @@ -1307,3 +1304,9 @@ impl FontMetricsProvider for LayoutFontMetricsProvider { } } } + +impl Debug for LayoutFontMetricsProvider { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("LayoutFontMetricsProvider").finish() + } +} diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 7a3401a3eac..99ed7b8dbe0 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -3536,6 +3536,7 @@ impl ScriptThread { script_chan: self.control_chan.clone(), image_cache: self.image_cache.clone(), font_cache_thread: self.font_cache_thread.clone(), + resource_threads: self.resource_threads.clone(), time_profiler_chan: self.time_profiler_chan.clone(), webrender_api_sender: self.webrender_api_sender.clone(), paint_time_metrics, diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 755538acf06..533abeeba73 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -72,7 +72,6 @@ use ipc_channel::ipc::{self, IpcSender}; use log::{error, trace, warn, Log, Metadata, Record}; use media::{GLPlayerThreads, GlApi, NativeDisplay, WindowGLContext}; use net::resource_thread::new_resource_threads; -use net_traits::IpcSend; use profile::{mem as profile_mem, time as profile_time}; use profile_traits::{mem, time}; use script::serviceworker_manager::ServiceWorkerManager; @@ -996,14 +995,14 @@ fn create_constellation( opts.ignore_certificate_errors, ); - let font_cache_thread = FontCacheThread::new( - public_resource_threads.sender(), - Box::new(FontCacheWR(compositor_proxy.clone())), - ); + let font_cache_thread = FontCacheThread::new(Box::new(WebRenderFontApiCompositorProxy( + compositor_proxy.clone(), + ))); let (canvas_create_sender, canvas_ipc_sender) = CanvasPaintThread::start( Box::new(CanvasWebrenderApi(compositor_proxy.clone())), font_cache_thread.clone(), + public_resource_threads.clone(), ); let initial_state = InitialConstellationState { @@ -1049,9 +1048,9 @@ fn create_constellation( ) } -struct FontCacheWR(CompositorProxy); +struct WebRenderFontApiCompositorProxy(CompositorProxy); -impl WebRenderFontApi for FontCacheWR { +impl WebRenderFontApi for WebRenderFontApiCompositorProxy { fn add_font_instance( &self, font_key: FontKey, @@ -1065,6 +1064,7 @@ impl WebRenderFontApi for FontCacheWR { ))); receiver.recv().unwrap() } + fn add_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey { let (sender, receiver) = unbounded(); let (bytes_sender, bytes_receiver) = @@ -1085,6 +1085,35 @@ impl WebRenderFontApi for FontCacheWR { ))); receiver.recv().unwrap() } + + fn forward_add_font_message( + &self, + bytes_receiver: ipc::IpcBytesReceiver, + font_index: u32, + result_sender: IpcSender<FontKey>, + ) { + let (sender, receiver) = unbounded(); + self.0 + .send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font( + FontToCompositorMsg::AddFont(sender, font_index, bytes_receiver), + ))); + let _ = result_sender.send(receiver.recv().unwrap()); + } + + fn forward_add_font_instance_message( + &self, + font_key: FontKey, + size: f32, + flags: FontInstanceFlags, + result_sender: IpcSender<FontInstanceKey>, + ) { + let (sender, receiver) = unbounded(); + self.0 + .send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font( + FontToCompositorMsg::AddFontInstance(font_key, size, flags, sender), + ))); + let _ = result_sender.send(receiver.recv().unwrap()); + } } #[derive(Clone)] diff --git a/components/shared/gfx/Cargo.toml b/components/shared/gfx/Cargo.toml index 23bf75bfb07..292cd58b92c 100644 --- a/components/shared/gfx/Cargo.toml +++ b/components/shared/gfx/Cargo.toml @@ -11,6 +11,7 @@ name = "gfx_traits" path = "lib.rs" [dependencies] +ipc-channel = { workspace = true } malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } range = { path = "../../range" } diff --git a/components/shared/net/lib.rs b/components/shared/net/lib.rs index 595d47c7b06..1fccc8a18c4 100644 --- a/components/shared/net/lib.rs +++ b/components/shared/net/lib.rs @@ -341,7 +341,7 @@ where // See also: https://github.com/servo/servo/blob/735480/components/script/script_thread.rs#L313 #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ResourceThreads { - core_thread: CoreResourceThread, + pub core_thread: CoreResourceThread, storage_thread: IpcSender<StorageThreadMsg>, } diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs index d07a009af0e..a15f0be9bde 100644 --- a/components/shared/script_layout/lib.rs +++ b/components/shared/script_layout/lib.rs @@ -29,6 +29,7 @@ use libc::c_void; use malloc_size_of_derive::MallocSizeOf; use metrics::PaintTimeMetrics; use net_traits::image_cache::{ImageCache, PendingImageId}; +use net_traits::ResourceThreads; use profile_traits::mem::Report; use profile_traits::time; use script_traits::{ @@ -164,6 +165,7 @@ pub struct LayoutConfig { pub constellation_chan: IpcSender<LayoutMsg>, pub script_chan: IpcSender<ConstellationControlMsg>, pub image_cache: Arc<dyn ImageCache>, + pub resource_threads: ResourceThreads, pub font_cache_thread: FontCacheThread, pub time_profiler_chan: time::ProfilerChan, pub webrender_api_sender: WebRenderScriptApi, diff --git a/components/shared/webrender/lib.rs b/components/shared/webrender/lib.rs index d4883d150e4..2eb252e37a4 100644 --- a/components/shared/webrender/lib.rs +++ b/components/shared/webrender/lib.rs @@ -188,6 +188,23 @@ pub trait WebRenderFontApi { flags: FontInstanceFlags, ) -> FontInstanceKey; fn add_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey; + /// Forward an already prepared `AddFont` message, sending it on to the compositor. This is used + /// to get WebRender [`FontKey`]s for web fonts in the per-layout `FontContext`. + fn forward_add_font_message( + &self, + bytes_receiver: IpcBytesReceiver, + font_index: u32, + result_sender: IpcSender<FontKey>, + ); + /// Forward an already prepared `AddFontInstance` message, sending it on to the compositor. This + /// is used to get WebRender [`FontInstanceKey`]s for web fonts in the per-layout `FontContext`. + fn forward_add_font_instance_message( + &self, + font_key: FontKey, + size: f32, + flags: FontInstanceFlags, + result_receiver: IpcSender<FontInstanceKey>, + ); fn add_system_font(&self, handle: NativeFontHandle) -> FontKey; } diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index a08d09d6500..3fc382143d9 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -174727,6 +174727,19 @@ {} ] ], + "downloadable-font-scoped-to-document.html": [ + "2dbc350069cd9c61925967655f83217b800fc9eb", + [ + null, + [ + [ + "/css/css-fonts/downloadable-font-scoped-to-document-ref.html", + "==" + ] + ], + {} + ] + ], "first-available-font-001.html": [ "5eb88f7bf6713b80e0adb4728681e62c3f2dc2bc", [ @@ -396276,6 +396289,10 @@ "28a92a86dcaf6bc9c45bb75fce4869bc0ae21c37", [] ], + "downloadable-font-scoped-to-document-ref.html": [ + "4d7da060cbf1a161aa1366f0bcb00b94e09502ad", + [] + ], "first-available-font-001-ref.html": [ "0acbd338e0ce9f558d2eaa2e48ad4be0524fb0ae", [] @@ -403399,6 +403416,18 @@ "6ed4aa506e95a35a065318f597547653bda52eb5", [] ], + "iframe-missing-font-face-rule.html": [ + "da97b781e8072923138e0160320e76d3013c3e53", + [] + ], + "iframe-using-ahem-as-web-font.html": [ + "b21066df8f57a8b11432a1168a62e7a4fbbe07b1", + [] + ], + "iframe-without-web-font.html": [ + "85e7fef282889894016088e082b623b92a436784", + [] + ], "js": { "font-variant-features.js": [ "4b56fee193956710b847ba79c5f9c3a5a7d15a33", diff --git a/tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document-ref.html b/tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document-ref.html new file mode 100644 index 00000000000..4d7da060cbf --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document-ref.html @@ -0,0 +1,17 @@ + <!DOCTYPE html> + +<html> + <head> + <title>CSS fonts: Web fonts loaded in a document are not available in other documents</title> + <link rel="author" title="Martin Robinson" href="mrobinson@igalia.com"> + <link rel="author" title="Mukilan Thiyagarajan" href="mukilan@igalia.com"> + </head> + + <body> + <p>Test passes if Ahem is only used in the first iframe.</p> + <iframe src="support/iframe-using-ahem-as-web-font.html"></iframe> + <iframe src="support/iframe-without-web-font.html"></iframe> + </body> + +</html> + diff --git a/tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document.html b/tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document.html new file mode 100644 index 00000000000..2dbc350069c --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document.html @@ -0,0 +1,28 @@ + <!DOCTYPE html> + +<html class="reftest-wait"> + <head> + <title>CSS fonts: Web fonts loaded in a document are not available in other documents</title> + <link rel="author" title="Martin Robinson" href="mrobinson@igalia.com"> + <link rel="author" title="Mukilan Thiyagarajan" href="mukilan@igalia.com"> + <link rel="match" href="downloadable-font-scoped-to-document-ref.html"> + <link rel="help" href="https://drafts.csswg.org/css-fonts/#font-face-rule"> + </head> + + <body> + <p>Test passes if Ahem is only used in the first iframe.</p> + <iframe id="iframe1" src="support/iframe-using-ahem-as-web-font.html"></iframe> + <iframe id="iframe2" src=""></iframe> + + <script> + // Delay the loading of the second iframe to make it more likely that the font + // has loaded properly into the first iframe. + iframe1.onload = () => { + iframe2.src ="support/iframe-missing-font-face-rule.html"; + document.documentElement.classList.remove('reftest-wait'); + }; + </script> + </body> + +</html> + diff --git a/tests/wpt/tests/css/css-fonts/support/iframe-missing-font-face-rule.html b/tests/wpt/tests/css/css-fonts/support/iframe-missing-font-face-rule.html new file mode 100644 index 00000000000..da97b781e80 --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/support/iframe-missing-font-face-rule.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<div style="font-size: 30px; font-family: CustomFontFamily">Hello!</div> diff --git a/tests/wpt/tests/css/css-fonts/support/iframe-using-ahem-as-web-font.html b/tests/wpt/tests/css/css-fonts/support/iframe-using-ahem-as-web-font.html new file mode 100644 index 00000000000..b21066df8f5 --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/support/iframe-using-ahem-as-web-font.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<style> +@font-face { + font-family: CustomFontFamily; + src: url(/fonts/Ahem.ttf); +} +</style> +<div style="font-size: 30px; font-family: CustomFontFamily">Hello!</div> diff --git a/tests/wpt/tests/css/css-fonts/support/iframe-without-web-font.html b/tests/wpt/tests/css/css-fonts/support/iframe-without-web-font.html new file mode 100644 index 00000000000..85e7fef2828 --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/support/iframe-without-web-font.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<div style="font-size: 30px;">Hello!</div> |