From ac567645a75630830a99d90946e0e96d0a759ead Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Wed, 25 Sep 2024 22:15:47 +0200 Subject: [PATCH] fonts: Simplify `FontContext` in two ways that affect the unit test (#33541) This is done by no longer forwarding compositor-bound messages through SystemFontService and making `FontContext` non-generic: - Messages from the `FontContext` to the `Compositor` no longer need to be forwarded through the `SystemFontService`. Instead send these messages directly through the script IPC channel to the `Compositor`. - Instead of adding a mock `SystemFontServiceProxy`, simply implement a mock `SystemFontService` on the other side of an IPC channel in the `font_context` unit test. This allows making `FontContext` non-generic, greatly simplifying the code. The extra complexity moves into the unit test. These changes necessitate adding a new kind of `FontIdentifier`, `FontIdentifier::Mock` due to the fact that local fonts have platform-specific identifiers. This avoids having to pretend like the system font service can have web fonts -- which was always a bit of a hack. These two changes are combined into one PR because they both require extensive and similar chages in the font_context unit test which dependended on the details of both of them. Signed-off-by: Martin Robinson --- components/canvas/canvas_data.rs | 6 +- components/canvas/canvas_paint_thread.rs | 13 +- components/compositing/compositor.rs | 110 ++++--- components/fonts/font.rs | 30 +- components/fonts/font_context.rs | 109 +++++-- components/fonts/font_store.rs | 18 +- components/fonts/font_template.rs | 28 +- .../platform/freetype/android/font_list.rs | 8 +- .../fonts/platform/freetype/font_list.rs | 10 +- .../fonts/platform/freetype/ohos/font_list.rs | 7 +- .../platform/macos/core_text_font_cache.rs | 2 +- components/fonts/platform/macos/font_list.rs | 8 +- .../fonts/platform/windows/font_list.rs | 7 +- components/fonts/system_font_service.rs | 132 +++------ components/fonts/tests/font_context.rs | 269 +++++++++++------- components/layout/context.rs | 6 +- components/layout/inline.rs | 8 +- components/layout/text.rs | 9 +- components/layout_2020/context.rs | 4 +- .../layout_2020/flow/inline/text_run.rs | 9 +- components/layout_thread/lib.rs | 8 +- components/layout_thread_2020/lib.rs | 10 +- components/servo/lib.rs | 56 +--- components/shared/compositing/lib.rs | 6 +- components/shared/webrender/lib.rs | 34 +-- 25 files changed, 482 insertions(+), 425 deletions(-) diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 33254113e56..4c58b269ca0 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -11,7 +11,7 @@ use euclid::default::{Box2D, Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::point2; use fonts::{ ByteIndex, FontBaseline, FontContext, FontGroup, FontMetrics, FontRef, GlyphInfo, GlyphStore, - ShapingFlags, ShapingOptions, SystemFontServiceProxy, LAST_RESORT_GLYPH_ADVANCE, + ShapingFlags, ShapingOptions, LAST_RESORT_GLYPH_ADVANCE, }; use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; use log::{debug, warn}; @@ -434,7 +434,7 @@ pub struct CanvasData<'a> { old_image_key: Option, /// An old webrender image key that can be deleted when the current epoch ends. very_old_image_key: Option, - font_context: Arc>, + font_context: Arc, } fn create_backend() -> Box { @@ -446,7 +446,7 @@ impl<'a> CanvasData<'a> { size: Size2D, webrender_api: Box, antialias: AntialiasMode, - font_context: Arc>, + font_context: Arc, ) -> CanvasData<'a> { let backend = create_backend(); let draw_target = backend.create_drawtarget(size); diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index 4f9007c9a52..5fec02a2b7a 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -17,7 +17,7 @@ use ipc_channel::router::ROUTER; use log::warn; use net_traits::ResourceThreads; use webrender_api::ImageKey; -use webrender_traits::ImageUpdate; +use webrender_traits::{ImageUpdate, WebRenderScriptApi}; use crate::canvas_data::*; @@ -37,7 +37,7 @@ pub struct CanvasPaintThread<'a> { canvases: HashMap>, next_canvas_id: CanvasId, webrender_api: Box, - font_context: Arc>, + font_context: Arc, } impl<'a> CanvasPaintThread<'a> { @@ -46,11 +46,18 @@ impl<'a> CanvasPaintThread<'a> { system_font_service: Arc, resource_threads: ResourceThreads, ) -> CanvasPaintThread<'a> { + // This is only used for web fonts and currently canvas never uses web fonts. + let webrender_script_api = WebRenderScriptApi::dummy(); + CanvasPaintThread { canvases: HashMap::new(), next_canvas_id: CanvasId(0), webrender_api, - font_context: Arc::new(FontContext::new(system_font_service, resource_threads)), + font_context: Arc::new(FontContext::new( + system_font_service, + webrender_script_api, + resource_threads, + )), } } diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index ab3f4ae0bfb..4271efd75a8 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -9,6 +9,7 @@ use std::fs::{create_dir_all, File}; use std::io::Write; use std::iter::once; use std::rc::Rc; +use std::sync::Arc; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use base::cross_process_instant::CrossProcessInstant; @@ -23,7 +24,7 @@ use embedder_traits::Cursor; use euclid::{Point2D, Rect, Scale, Transform3D, Vector2D}; use fnv::{FnvHashMap, FnvHashSet}; use image::{DynamicImage, ImageFormat}; -use ipc_channel::ipc; +use ipc_channel::ipc::{self, IpcSharedMemory}; use libc::c_void; use log::{debug, error, info, trace, warn}; use pixels::{CorsStatus, Image, PixelFormat}; @@ -43,9 +44,10 @@ use webrender_api::units::{ }; use webrender_api::{ self, BuiltDisplayList, DirtyRect, DisplayListPayload, DocumentId, Epoch as WebRenderEpoch, - ExternalScrollId, FontInstanceOptions, HitTestFlags, PipelineId as WebRenderPipelineId, - PropertyBinding, ReferenceFrameKind, RenderReasons, SampledScrollOffset, ScrollLocation, - SpaceAndClipInfo, SpatialId, SpatialTreeItemKey, TransformStyle, + ExternalScrollId, FontInstanceFlags, FontInstanceKey, FontInstanceOptions, FontKey, + HitTestFlags, PipelineId as WebRenderPipelineId, PropertyBinding, ReferenceFrameKind, + RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo, SpatialId, + SpatialTreeItemKey, TransformStyle, }; use webrender_traits::display_list::{HitTestInfo, ScrollTree}; use webrender_traits::{ @@ -841,6 +843,23 @@ impl IOCompositor { .send_transaction(self.webrender_document, txn); }, + ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::AddFont( + data, + index, + key_sender, + )) => { + let _ = key_sender.send(self.add_font(index, data)); + }, + + ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::AddFontInstance( + font_key, + size, + flags, + sender, + )) => { + let _ = sender.send(self.add_font_instance(font_key, size, flags)); + }, + ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::RemoveFonts( keys, instance_keys, @@ -865,47 +884,24 @@ impl IOCompositor { .send_transaction(self.webrender_document, txn); }, - ForwardedToCompositorMsg::Font(FontToCompositorMsg::AddFontInstance( + ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFontInstance( font_key, size, flags, sender, )) => { - let key = self.webrender_api.generate_font_instance_key(); - let mut transaction = Transaction::new(); - - let font_instance_options = FontInstanceOptions { - flags, - ..Default::default() - }; - transaction.add_font_instance( - key, - font_key, - size, - Some(font_instance_options), - None, - Vec::new(), - ); - - self.webrender_api - .send_transaction(self.webrender_document, transaction); - let _ = sender.send(key); + let _ = sender.send(self.add_font_instance(font_key, size, flags)); }, - ForwardedToCompositorMsg::Font(FontToCompositorMsg::AddFont( + ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFont( key_sender, index, data, )) => { - let font_key = self.webrender_api.generate_font_key(); - let mut transaction = Transaction::new(); - transaction.add_raw_font(font_key, (**data).into(), index); - self.webrender_api - .send_transaction(self.webrender_document, transaction); - let _ = key_sender.send(font_key); + let _ = key_sender.send(self.add_font(index, data)); }, - ForwardedToCompositorMsg::Font(FontToCompositorMsg::AddSystemFont( + ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddSystemFont( key_sender, native_handle, )) => { @@ -960,16 +956,26 @@ impl IOCompositor { self.remove_pipeline_root_layer(pipeline_id); let _ = sender.send(()); }, - CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font( + CompositorMsg::Forwarded(ForwardedToCompositorMsg::SystemFontService( FontToCompositorMsg::AddFontInstance(_, _, _, sender), )) => { let _ = sender.send(self.webrender_api.generate_font_instance_key()); }, - CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font( + CompositorMsg::Forwarded(ForwardedToCompositorMsg::Layout( + ScriptToCompositorMsg::AddFontInstance(_, _, _, sender), + )) => { + let _ = sender.send(self.webrender_api.generate_font_instance_key()); + }, + CompositorMsg::Forwarded(ForwardedToCompositorMsg::SystemFontService( FontToCompositorMsg::AddFont(sender, _, _), )) => { let _ = sender.send(self.webrender_api.generate_font_key()); }, + CompositorMsg::Forwarded(ForwardedToCompositorMsg::Layout( + ScriptToCompositorMsg::AddFont(_, _, sender), + )) => { + let _ = sender.send(self.webrender_api.generate_font_key()); + }, CompositorMsg::Forwarded(ForwardedToCompositorMsg::Canvas( CanvasToCompositorMsg::GenerateKey(sender), )) => { @@ -2518,4 +2524,40 @@ impl IOCompositor { eprintln!("Unable to write servo version for WebRender Capture: {error:?}"); } } + + fn add_font_instance( + &mut self, + font_key: FontKey, + size: f32, + flags: FontInstanceFlags, + ) -> FontInstanceKey { + let instance_key = self.webrender_api.generate_font_instance_key(); + let mut transaction = Transaction::new(); + + let font_instance_options = FontInstanceOptions { + flags, + ..Default::default() + }; + transaction.add_font_instance( + instance_key, + font_key, + size, + Some(font_instance_options), + None, + Vec::new(), + ); + + self.webrender_api + .send_transaction(self.webrender_document, transaction); + instance_key + } + + fn add_font(&mut self, index: u32, data: Arc) -> FontKey { + let font_key = self.webrender_api.generate_font_key(); + let mut transaction = Transaction::new(); + transaction.add_raw_font(font_key, (**data).into(), index); + self.webrender_api + .send_transaction(self.webrender_document, transaction); + font_key + } } diff --git a/components/fonts/font.rs b/components/fonts/font.rs index b4d3df30b6e..47de63bad2c 100644 --- a/components/fonts/font.rs +++ b/components/fonts/font.rs @@ -31,7 +31,7 @@ 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; -use crate::system_font_service::{FontIdentifier, SystemFontServiceProxyTrait}; +use crate::system_font_service::FontIdentifier; use crate::{ ByteIndex, EmojiPresentationPreference, FallbackFontSelectionOptions, FontData, GlyphData, GlyphId, GlyphStore, Shaper, @@ -550,9 +550,9 @@ impl FontGroup { /// `codepoint`. If no such font is found, returns the first available font or fallback font /// (which will cause a "glyph not found" character to be rendered). If no font at all can be /// found, returns None. - pub fn find_by_codepoint( + pub fn find_by_codepoint( &mut self, - font_context: &FontContext, + font_context: &FontContext, codepoint: char, next_codepoint: Option, ) -> Option { @@ -622,10 +622,7 @@ impl FontGroup { } /// Find the first available font in the group, or the first available fallback font. - pub fn first( - &mut self, - font_context: &FontContext, - ) -> Option { + pub fn first(&mut self, font_context: &FontContext) -> Option { // From https://drafts.csswg.org/css-fonts/#first-available-font: // > The first available font, used for example in the definition of font-relative lengths // > such as ex or in the definition of the line-height property, is defined to be the first @@ -649,14 +646,13 @@ impl FontGroup { /// Attempts to find a font which matches the given `template_predicate` and `font_predicate`. /// This method mutates because we may need to load new font data in the process of finding /// a suitable font. - fn find( + fn find( &mut self, - font_context: &FontContext, + font_context: &FontContext, template_predicate: TemplatePredicate, font_predicate: FontPredicate, ) -> Option where - S: SystemFontServiceProxyTrait, TemplatePredicate: Fn(FontTemplateRef) -> bool, FontPredicate: Fn(&FontRef) -> bool, { @@ -678,15 +674,14 @@ impl FontGroup { /// `font_predicate`. The default family (i.e. "serif") will be tried first, followed by /// platform-specific family names. If a `codepoint` is provided, then its Unicode block may be /// used to refine the list of family names which will be tried. - fn find_fallback( + fn find_fallback( &mut self, - font_context: &FontContext, + font_context: &FontContext, options: FallbackFontSelectionOptions, template_predicate: TemplatePredicate, font_predicate: FontPredicate, ) -> Option where - S: SystemFontServiceProxyTrait, TemplatePredicate: Fn(FontTemplateRef) -> bool, FontPredicate: Fn(&FontRef) -> bool, { @@ -750,15 +745,14 @@ impl FontGroupFamily { } } - fn find( + fn find( &mut self, font_descriptor: &FontDescriptor, - font_context: &FontContext, + font_context: &FontContext, template_predicate: &TemplatePredicate, font_predicate: &FontPredicate, ) -> Option where - S: SystemFontServiceProxyTrait, TemplatePredicate: Fn(FontTemplateRef) -> bool, FontPredicate: Fn(&FontRef) -> bool, { @@ -781,10 +775,10 @@ impl FontGroupFamily { .next() } - fn members( + fn members( &mut self, font_descriptor: &FontDescriptor, - font_context: &FontContext, + font_context: &FontContext, ) -> impl Iterator { let family_descriptor = &self.family_descriptor; let members = self.members.get_or_insert_with(|| { diff --git a/components/fonts/font_context.rs b/components/fonts/font_context.rs index cdd97466341..896007c5829 100644 --- a/components/fonts/font_context.rs +++ b/components/fonts/font_context.rs @@ -12,6 +12,7 @@ use app_units::Au; use crossbeam_channel::unbounded; use fnv::FnvHasher; use fonts_traits::WebFontLoadFinishedCallback; +use ipc_channel::ipc; use log::{debug, trace}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of_derive::MallocSizeOf; @@ -29,7 +30,8 @@ use style::stylesheets::{CssRule, DocumentStyleSheet, FontFaceRule, StylesheetIn use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFontFamily}; use style::Atom; use url::Url; -use webrender_api::{FontInstanceKey, FontKey}; +use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey}; +use webrender_traits::{ScriptToCompositorMsg, WebRenderScriptApi}; use crate::font::{ Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope, @@ -37,10 +39,8 @@ use crate::font::{ use crate::font_store::{CrossThreadFontStore, CrossThreadWebRenderFontStore}; use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods}; use crate::platform::font::PlatformFont; -use crate::system_font_service::{ - CSSFontFaceDescriptors, FontIdentifier, SystemFontServiceProxyTrait, -}; -use crate::{FontData, LowercaseFontFamilyName, PlatformFontMethods}; +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) @@ -48,10 +48,13 @@ static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h) /// 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 { - pub(crate) system_font_service_proxy: Arc, +pub struct FontContext { + pub(crate) system_font_service_proxy: Arc, resource_threads: ReentrantMutex, + /// A sender that can send messages and receive replies from the compositor. + webrender_api: ReentrantMutex, + /// 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>>, @@ -67,7 +70,7 @@ pub struct FontContext { have_removed_web_fonts: AtomicBool, } -impl MallocSizeOf for FontContext { +impl MallocSizeOf for FontContext { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { let font_cache_size = self .fonts @@ -87,12 +90,17 @@ impl MallocSizeOf for FontContext { } } -impl FontContext { - pub fn new(system_font_service_proxy: Arc, resource_threads: ResourceThreads) -> Self { +impl FontContext { + pub fn new( + system_font_service_proxy: Arc, + webrender_api: WebRenderScriptApi, + resource_threads: ResourceThreads, + ) -> Self { #[allow(clippy::default_constructed_unit_structs)] Self { system_font_service_proxy, resource_threads: ReentrantMutex::new(resource_threads.core_thread), + webrender_api: ReentrantMutex::new(webrender_api), fonts: Default::default(), resolved_font_groups: Default::default(), web_fonts: Arc::new(RwLock::default()), @@ -106,11 +114,13 @@ impl FontContext { } pub(crate) fn get_font_data(&self, identifier: &FontIdentifier) -> Arc { - self.web_fonts - .read() - .get_font_data(identifier) - .or_else(|| self.system_font_service_proxy.get_font_data(identifier)) - .expect("Could not find font data") + match identifier { + FontIdentifier::Web(_) => self.web_fonts.read().get_font_data(identifier), + FontIdentifier::Local(_) | FontIdentifier::Mock(_) => { + self.system_font_service_proxy.get_font_data(identifier) + }, + } + .expect("Could not find font data") } /// Handle the situation where a web font finishes loading, specifying if the load suceeded or failed. @@ -272,11 +282,13 @@ impl FontContext { )?; font.font_key = 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::Local(_) | FontIdentifier::Mock(_) => { + self.system_font_service_proxy.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( self, font_template.clone(), @@ -291,6 +303,42 @@ impl FontContext { fn invalidate_font_groups_after_web_font_load(&self) { self.resolved_font_groups.write().clear(); } + + pub(crate) fn get_web_font(&self, data: Arc, index: u32) -> FontKey { + let (result_sender, result_receiver) = + ipc::channel().expect("failed to create IPC channel"); + let _ = self + .webrender_api + .lock() + .sender() + .send(ScriptToCompositorMsg::AddFont( + data.as_ipc_shared_memory(), + index, + result_sender, + )); + result_receiver.recv().unwrap() + } + + pub(crate) 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 + .webrender_api + .lock() + .sender() + .send(ScriptToCompositorMsg::AddFontInstance( + font_key, + font_size, + font_flags, + result_sender, + )); + result_receiver.recv().unwrap() + } } #[derive(Clone)] @@ -318,7 +366,7 @@ pub trait FontContextWebFontMethods { -> (Vec, Vec); } -impl FontContextWebFontMethods for Arc> { +impl FontContextWebFontMethods for Arc { fn add_all_web_fonts_from_stylesheet( &self, stylesheet: &DocumentStyleSheet, @@ -523,8 +571,8 @@ impl FontContextWebFontMethods for Arc } } -struct RemoteWebFontDownloader { - font_context: Arc>, +struct RemoteWebFontDownloader { + font_context: Arc, url: ServoArc, web_font_family_name: LowercaseFontFamilyName, response_valid: Mutex, @@ -537,10 +585,10 @@ enum DownloaderResponseResult { Failure, } -impl RemoteWebFontDownloader { +impl RemoteWebFontDownloader { fn download( url_source: UrlSource, - font_context: Arc>, + font_context: Arc, web_font_family_name: LowercaseFontFamilyName, state: WebFontDownloadState, ) { @@ -628,12 +676,11 @@ impl RemoteWebFontDownloader { Arc::new(FontData::from_bytes(local_identifier.read_data_from_file())) }, - FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."), + _ => unreachable!("Web and mock fonts should always have data."), }) } @@ -196,9 +194,9 @@ pub struct WebRenderFontStore { pub(crate) type CrossThreadWebRenderFontStore = Arc>; impl WebRenderFontStore { - pub(crate) fn get_font_instance( + pub(crate) fn get_font_instance( &mut self, - font_context: &FontContext, + font_context: &FontContext, font_template: FontTemplateRef, pt_size: Au, flags: FontInstanceFlags, @@ -210,18 +208,14 @@ impl WebRenderFontStore { .entry(identifier.clone()) .or_insert_with(|| { let data = font_context.get_font_data(&identifier); - font_context - .system_font_service_proxy - .get_web_font(data, identifier.index()) + font_context.get_web_font(data, identifier.index()) }); *self .webrender_font_instance_map .entry((font_key, pt_size)) .or_insert_with(|| { - font_context - .system_font_service_proxy - .get_web_font_instance(font_key, pt_size.to_f32_px(), flags) + font_context.get_web_font_instance(font_key, pt_size.to_f32_px(), flags) }) } diff --git a/components/fonts/font_template.rs b/components/fonts/font_template.rs index d053fe9f2d4..d7733f66274 100644 --- a/components/fonts/font_template.rs +++ b/components/fonts/font_template.rs @@ -9,14 +9,12 @@ use std::sync::Arc; use atomic_refcell::AtomicRefCell; use malloc_size_of_derive::MallocSizeOf; use serde::{Deserialize, Serialize}; -use servo_url::ServoUrl; use style::computed_values::font_stretch::T as FontStretch; use style::computed_values::font_style::T as FontStyle; use style::stylesheets::DocumentStyleSheet; use style::values::computed::font::FontWeight; use crate::font::FontDescriptor; -use crate::platform::font_list::LocalFontIdentifier; use crate::system_font_service::{ CSSFontFaceDescriptors, ComputedFontStyleDescriptor, FontIdentifier, }; @@ -160,29 +158,17 @@ impl Debug for FontTemplate { /// is common, regardless of the number of instances of /// this font handle per thread. impl FontTemplate { - /// Create a new [`FontTemplate`] for a system font installed locally. - pub fn new_for_local_font( - identifier: LocalFontIdentifier, - descriptor: FontTemplateDescriptor, - ) -> FontTemplate { - FontTemplate { - identifier: FontIdentifier::Local(identifier), - descriptor, - stylesheet: None, - } - } - - /// Create a new [`FontTemplate`] for a `@font-family` with a `url(...)` `src` font. - pub fn new_for_remote_web_font( - url: ServoUrl, + /// Create a new [`FontTemplate`]. + pub fn new( + identifier: FontIdentifier, descriptor: FontTemplateDescriptor, stylesheet: Option, - ) -> Result { - Ok(FontTemplate { - identifier: FontIdentifier::Web(url), + ) -> FontTemplate { + FontTemplate { + identifier, descriptor, stylesheet, - }) + } } /// Create a new [`FontTemplate`] for a `@font-family` with a `local(...)` `src`. This takes in diff --git a/components/fonts/platform/freetype/android/font_list.rs b/components/fonts/platform/freetype/android/font_list.rs index c52b06d8b03..34d4bdeeeae 100644 --- a/components/fonts/platform/freetype/android/font_list.rs +++ b/components/fonts/platform/freetype/android/font_list.rs @@ -19,7 +19,8 @@ use style::Atom; use super::xml::{Attribute, Node}; use crate::{ - FallbackFontSelectionOptions, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName, + FallbackFontSelectionOptions, FontIdentifier, FontTemplate, FontTemplateDescriptor, + LowercaseFontFamilyName, }; static FONT_LIST: LazyLock = LazyLock::new(|| FontList::new()); @@ -491,9 +492,10 @@ where None => StyleFontStyle::NORMAL, }; let descriptor = FontTemplateDescriptor::new(weight, stretch, style); - callback(FontTemplate::new_for_local_font( - local_font_identifier, + callback(FontTemplate::new( + FontIdentifier::Local(local_font_identifier), descriptor, + None, )); }; diff --git a/components/fonts/platform/freetype/font_list.rs b/components/fonts/platform/freetype/font_list.rs index 0212344ba45..b395173de27 100644 --- a/components/fonts/platform/freetype/font_list.rs +++ b/components/fonts/platform/freetype/font_list.rs @@ -36,7 +36,10 @@ use super::c_str_to_string; use crate::font::map_platform_values_to_style_values; use crate::font_template::{FontTemplate, FontTemplateDescriptor}; use crate::platform::add_noto_fallback_families; -use crate::{EmojiPresentationPreference, FallbackFontSelectionOptions, LowercaseFontFamilyName}; +use crate::{ + EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, + LowercaseFontFamilyName, +}; /// An identifier for a local font on systems using Freetype. #[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] @@ -155,9 +158,10 @@ where }; let descriptor = FontTemplateDescriptor::new(weight, stretch, style); - callback(FontTemplate::new_for_local_font( - local_font_identifier, + callback(FontTemplate::new( + FontIdentifier::Local(local_font_identifier), descriptor, + None, )) } diff --git a/components/fonts/platform/freetype/ohos/font_list.rs b/components/fonts/platform/freetype/ohos/font_list.rs index 82a5740f124..f3d3218dee1 100644 --- a/components/fonts/platform/freetype/ohos/font_list.rs +++ b/components/fonts/platform/freetype/ohos/font_list.rs @@ -22,7 +22,7 @@ use style::Atom; use unicode_script::Script; use crate::{ - EmojiPresentationPreference, FallbackFontSelectionOptions, FontTemplate, + EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName, }; @@ -492,9 +492,10 @@ where None => StyleFontStyle::NORMAL, }; let descriptor = FontTemplateDescriptor::new(weight, stretch, style); - callback(FontTemplate::new_for_local_font( - local_font_identifier, + callback(FontTemplate::new( + FontIdentifier::Local(local_font_identifier), descriptor, + None, )); }; diff --git a/components/fonts/platform/macos/core_text_font_cache.rs b/components/fonts/platform/macos/core_text_font_cache.rs index 0574ff78325..50bf495c908 100644 --- a/components/fonts/platform/macos/core_text_font_cache.rs +++ b/components/fonts/platform/macos/core_text_font_cache.rs @@ -86,7 +86,7 @@ impl CoreTextFontCache { core_text::font::new_from_descriptor(&descriptor, clamped_pt_size) }, - FontIdentifier::Web(_) => { + FontIdentifier::Web(_) | FontIdentifier::Mock(_) => { let provider = CGDataProvider::from_buffer(data); let cgfont = CGFont::from_data_provider(provider).ok()?; core_text::font::new_from_CGFont(&cgfont, clamped_pt_size) diff --git a/components/fonts/platform/macos/font_list.rs b/components/fonts/platform/macos/font_list.rs index a2d12c4247f..d93224eeab7 100644 --- a/components/fonts/platform/macos/font_list.rs +++ b/components/fonts/platform/macos/font_list.rs @@ -18,7 +18,7 @@ use webrender_api::NativeFontHandle; use crate::platform::add_noto_fallback_families; use crate::platform::font::CoreTextFontTraitsMapping; use crate::{ - EmojiPresentationPreference, FallbackFontSelectionOptions, FontTemplate, + EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName, }; @@ -85,7 +85,11 @@ where postscript_name: Atom::from(family_descriptor.font_name()), path: Atom::from(path), }; - callback(FontTemplate::new_for_local_font(identifier, descriptor)); + callback(FontTemplate::new( + FontIdentifier::Local(identifier), + descriptor, + None, + )); } } } diff --git a/components/fonts/platform/windows/font_list.rs b/components/fonts/platform/windows/font_list.rs index 3a2c32e66be..4360db0021a 100644 --- a/components/fonts/platform/windows/font_list.rs +++ b/components/fonts/platform/windows/font_list.rs @@ -14,7 +14,7 @@ use style::values::computed::{FontStyle as StyleFontStyle, FontWeight as StyleFo use style::values::specified::font::FontStretchKeyword; use crate::{ - EmojiPresentationPreference, FallbackFontSelectionOptions, FontTemplate, + EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName, }; @@ -78,9 +78,10 @@ where let local_font_identifier = LocalFontIdentifier { font_descriptor: Arc::new(font.to_descriptor()), }; - callback(FontTemplate::new_for_local_font( - local_font_identifier, + callback(FontTemplate::new( + FontIdentifier::Local(local_font_identifier), template_descriptor, + None, )) } } diff --git a/components/fonts/system_font_service.rs b/components/fonts/system_font_service.rs index 8bf15eaa2eb..e1070b49021 100644 --- a/components/fonts/system_font_service.rs +++ b/components/fonts/system_font_service.rs @@ -24,6 +24,7 @@ use style::values::computed::font::{ }; use style::values::computed::{FontStretch, FontWeight}; use style::values::specified::FontStretch as SpecifiedFontStretch; +use style::Atom; use tracing::{span, Level}; use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey}; use webrender_traits::WebRenderFontApi; @@ -41,26 +42,27 @@ use crate::FontData; pub enum FontIdentifier { Local(LocalFontIdentifier), Web(ServoUrl), + Mock(Atom), } impl FontIdentifier { pub fn index(&self) -> u32 { match *self { Self::Local(ref local_font_identifier) => local_font_identifier.index(), - Self::Web(_) => 0, + Self::Web(_) | Self::Mock(_) => 0, } } } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct FontTemplateRequestResult { - templates: Vec, - template_data: Vec<(FontIdentifier, Arc)>, + pub templates: Vec, + pub template_data: Vec<(FontIdentifier, Arc)>, } /// Commands that the `FontContext` sends to the `SystemFontService`. #[derive(Debug, Deserialize, Serialize)] -pub enum Command { +pub enum SystemFontServiceMessage { GetFontTemplates( Option, SingleFontFamily, @@ -72,8 +74,6 @@ pub enum Command { FontInstanceFlags, IpcSender, ), - GetWebFont(Arc, u32, IpcSender), - GetWebFontInstance(FontKey, f32, FontInstanceFlags, IpcSender), Exit(IpcSender<()>), Ping, } @@ -93,7 +93,7 @@ struct ResolvedGenericFontFamilies { /// responsible for reading the list of system fonts, handling requests to match against /// them, and ensuring that only one copy of system font data is loaded at a time. pub struct SystemFontService { - port: IpcReceiver, + port: IpcReceiver, local_families: FontStore, webrender_api: Box, webrender_fonts: HashMap, @@ -102,7 +102,7 @@ pub struct SystemFontService { } #[derive(Clone, Deserialize, Serialize)] -pub struct SystemFontServiceProxySender(IpcSender); +pub struct SystemFontServiceProxySender(pub IpcSender); impl SystemFontServiceProxySender { pub fn to_proxy(&self) -> SystemFontServiceProxy { @@ -145,37 +145,21 @@ impl SystemFontService { let msg = self.port.recv().unwrap(); match msg { - Command::GetFontTemplates(font_descriptor, font_family, result_sender) => { + SystemFontServiceMessage::GetFontTemplates( + font_descriptor, + font_family, + result_sender, + ) => { let span = span!(Level::TRACE, "GetFontTemplates", servo_profiling = true); let _span = span.enter(); let _ = result_sender.send(self.get_font_templates(font_descriptor, font_family)); }, - Command::GetFontInstance(identifier, pt_size, flags, result) => { + SystemFontServiceMessage::GetFontInstance(identifier, pt_size, flags, result) => { let _ = result.send(self.get_font_instance(identifier, pt_size, flags)); }, - Command::GetWebFont(data, font_index, result_sender) => { - self.webrender_api.forward_add_font_message( - data.as_ipc_shared_memory(), - font_index, - result_sender, - ); - }, - 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) => { + SystemFontServiceMessage::Ping => (), + SystemFontServiceMessage::Exit(result) => { let _ = result.send(()); break; }, @@ -325,40 +309,17 @@ impl SystemFontService { } } -/// A trait for accessing the [`SystemFontServiceProxy`] necessary for unit testing. -pub trait SystemFontServiceProxyTrait: Send + Sync { - fn find_matching_font_templates( - &self, - descriptor_to_match: Option<&FontDescriptor>, - font_family_name: &SingleFontFamily, - ) -> Vec; - fn get_system_font_instance( - &self, - font_identifier: FontIdentifier, - size: Au, - flags: FontInstanceFlags, - ) -> FontInstanceKey; - fn get_web_font(&self, data: Arc, index: u32) -> FontKey; - fn get_web_font_instance( - &self, - font_key: FontKey, - size: f32, - flags: FontInstanceFlags, - ) -> FontInstanceKey; - fn get_font_data(&self, identifier: &FontIdentifier) -> Option>; -} - #[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)] struct FontTemplateCacheKey { font_descriptor: Option, family_descriptor: SingleFontFamily, } -/// The public interface to the [`SystemFontService`], used by per-Document `FontContext` -/// instances (via [`SystemFontServiceProxyTrait`]). +/// The public interface to the [`SystemFontService`], used by per-Document +/// `FontContext` instances. #[derive(Debug)] pub struct SystemFontServiceProxy { - sender: ReentrantMutex>, + sender: ReentrantMutex>, templates: RwLock>>, data_cache: RwLock>>, } @@ -453,7 +414,7 @@ impl SystemFontServiceProxy { let (response_chan, response_port) = ipc::channel().unwrap(); self.sender .lock() - .send(Command::Exit(response_chan)) + .send(SystemFontServiceMessage::Exit(response_chan)) .expect("Couldn't send SystemFontService exit message"); response_port .recv() @@ -463,10 +424,8 @@ impl SystemFontServiceProxy { pub fn to_sender(&self) -> SystemFontServiceProxySender { SystemFontServiceProxySender(self.sender.lock().clone()) } -} -impl SystemFontServiceProxyTrait for SystemFontServiceProxy { - fn get_system_font_instance( + pub(crate) fn get_system_font_instance( &self, identifier: FontIdentifier, size: Au, @@ -475,7 +434,7 @@ impl SystemFontServiceProxyTrait for SystemFontServiceProxy { let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); self.sender .lock() - .send(Command::GetFontInstance( + .send(SystemFontServiceMessage::GetFontInstance( identifier, size, flags, @@ -485,7 +444,11 @@ impl SystemFontServiceProxyTrait for SystemFontServiceProxy { let instance_key = response_port.recv(); if instance_key.is_err() { - let font_thread_has_closed = self.sender.lock().send(Command::Ping).is_err(); + let font_thread_has_closed = self + .sender + .lock() + .send(SystemFontServiceMessage::Ping) + .is_err(); assert!( font_thread_has_closed, "Failed to receive a response from live font cache" @@ -495,7 +458,7 @@ impl SystemFontServiceProxyTrait for SystemFontServiceProxy { instance_key.unwrap() } - fn find_matching_font_templates( + pub(crate) fn find_matching_font_templates( &self, descriptor_to_match: Option<&FontDescriptor>, family_descriptor: &SingleFontFamily, @@ -516,7 +479,7 @@ impl SystemFontServiceProxyTrait for SystemFontServiceProxy { let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); self.sender .lock() - .send(Command::GetFontTemplates( + .send(SystemFontServiceMessage::GetFontTemplates( descriptor_to_match.cloned(), family_descriptor.clone(), response_chan, @@ -525,7 +488,11 @@ impl SystemFontServiceProxyTrait for SystemFontServiceProxy { let reply = response_port.recv(); let Ok(reply) = reply else { - let font_thread_has_closed = self.sender.lock().send(Command::Ping).is_err(); + let font_thread_has_closed = self + .sender + .lock() + .send(SystemFontServiceMessage::Ping) + .is_err(); assert!( font_thread_has_closed, "Failed to receive a response from live font cache" @@ -544,34 +511,7 @@ impl SystemFontServiceProxyTrait for SystemFontServiceProxy { templates } - fn get_web_font(&self, data: Arc, index: u32) -> FontKey { - let (result_sender, result_receiver) = - ipc::channel().expect("failed to create IPC channel"); - let _ = self - .sender - .lock() - .send(Command::GetWebFont(data, index, result_sender)); - 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.sender.lock().send(Command::GetWebFontInstance( - font_key, - font_size, - font_flags, - result_sender, - )); - result_receiver.recv().unwrap() - } - - fn get_font_data(&self, identifier: &FontIdentifier) -> Option> { + pub(crate) fn get_font_data(&self, identifier: &FontIdentifier) -> Option> { self.data_cache.read().get(identifier).cloned() } } diff --git a/components/fonts/tests/font_context.rs b/components/fonts/tests/font_context.rs index 470155a2871..de706451569 100644 --- a/components/fonts/tests/font_context.rs +++ b/components/fonts/tests/font_context.rs @@ -8,20 +8,21 @@ use std::io::prelude::*; use std::path::PathBuf; use std::sync::atomic::{AtomicI32, Ordering}; use std::sync::Arc; +use std::thread; use app_units::Au; use fonts::platform::font::PlatformFont; use fonts::{ fallback_font_families, FallbackFontSelectionOptions, FontContext, FontData, FontDescriptor, - FontFamilyDescriptor, FontIdentifier, FontSearchScope, FontTemplate, FontTemplateRef, - FontTemplates, PlatformFontMethods, SystemFontServiceProxyTrait, + FontFamilyDescriptor, FontIdentifier, FontSearchScope, FontTemplate, FontTemplateRequestResult, + FontTemplates, PlatformFontMethods, SystemFontServiceMessage, SystemFontServiceProxy, + SystemFontServiceProxySender, }; -use ipc_channel::ipc; +use ipc_channel::ipc::{self, IpcReceiver}; use net_traits::ResourceThreads; use parking_lot::Mutex; use servo_arc::Arc as ServoArc; use servo_atoms::Atom; -use servo_url::ServoUrl; use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps; use style::properties::style_structs::Font as FontStyleStruct; use style::values::computed::font::{ @@ -31,15 +32,111 @@ use style::values::computed::font::{ use style::values::computed::{FontLanguageOverride, XLang}; use style::values::generics::font::LineHeight; use style::ArcSlice; -use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, IdNamespace}; +use webrender_api::{FontInstanceKey, IdNamespace}; +use webrender_traits::WebRenderScriptApi; -struct MockFontCacheThread { +struct TestContext { + context: FontContext, + system_font_service: Arc, + system_font_service_proxy: SystemFontServiceProxy, +} + +impl TestContext { + fn new() -> TestContext { + let (system_font_service, system_font_service_proxy) = MockSystemFontService::spawn(); + let (core_sender, _) = ipc::channel().unwrap(); + let (storage_sender, _) = ipc::channel().unwrap(); + let mock_resource_threads = ResourceThreads::new(core_sender, storage_sender); + let mock_webrender_api = WebRenderScriptApi::dummy(); + + let proxy_clone = Arc::new(system_font_service_proxy.to_sender().to_proxy()); + Self { + context: FontContext::new(proxy_clone, mock_webrender_api, mock_resource_threads), + system_font_service, + system_font_service_proxy, + } + } +} + +impl Drop for TestContext { + fn drop(&mut self) { + self.system_font_service_proxy.exit(); + } +} + +struct MockSystemFontService { families: Mutex>, data: Mutex>>, find_font_count: AtomicI32, } -impl MockFontCacheThread { +impl MockSystemFontService { + pub fn spawn() -> (Arc, SystemFontServiceProxy) { + let (sender, receiver) = ipc::channel().unwrap(); + let system_font_service = Arc::new(Self::new()); + + let system_font_service_clone = system_font_service.clone(); + thread::Builder::new() + .name("MockSystemFontService".to_owned()) + .spawn(move || system_font_service_clone.run(receiver)) + .expect("Thread spawning failed"); + ( + system_font_service, + SystemFontServiceProxySender(sender).to_proxy(), + ) + } + + fn run(&self, receiver: IpcReceiver) { + loop { + match receiver.recv().unwrap() { + SystemFontServiceMessage::GetFontTemplates( + descriptor_to_match, + font_family, + result_sender, + ) => { + self.find_font_count.fetch_add(1, Ordering::Relaxed); + + let SingleFontFamily::FamilyName(family_name) = font_family else { + let _ = result_sender.send(FontTemplateRequestResult::default()); + continue; + }; + + let templates: Vec<_> = self + .families + .lock() + .get_mut(&*family_name.name) + .map(|family| family.find_for_descriptor(descriptor_to_match.as_ref())) + .unwrap() + .into_iter() + .map(|template| template.borrow().clone()) + .collect(); + + let template_data = templates + .iter() + .map(|template| { + let identifier = template.identifier().clone(); + let data = self.data.lock().get(&identifier).unwrap().clone(); + (identifier, data) + }) + .collect(); + + let _ = result_sender.send(FontTemplateRequestResult { + templates, + template_data, + }); + }, + SystemFontServiceMessage::GetFontInstance(_, _, _, result_sender) => { + let _ = result_sender.send(FontInstanceKey(IdNamespace(0), 0)); + }, + SystemFontServiceMessage::Exit(result_sender) => { + let _ = result_sender.send(()); + break; + }, + SystemFontServiceMessage::Ping => {}, + } + } + } + fn new() -> Self { let proxy = Self { families: Default::default(), @@ -69,18 +166,6 @@ impl MockFontCacheThread { proxy } - fn identifier_for_font_name(name: &str) -> FontIdentifier { - FontIdentifier::Web(Self::url_for_font_name(name)) - } - - fn url_for_font_name(name: &str) -> ServoUrl { - let mut path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "tests", "support", "CSSTest"] - .iter() - .collect(); - path.push(format!("{}.ttf", name)); - ServoUrl::from_file_path(path).unwrap() - } - fn add_face(&self, family: &mut FontTemplates, name: &str) { let mut path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "tests", "support", "CSSTest"] .iter() @@ -92,65 +177,20 @@ impl MockFontCacheThread { file.bytes().map(|b| b.unwrap()).collect(), )); - let url = Self::url_for_font_name(name); - let identifier = FontIdentifier::Web(url.clone()); + let identifier = FontIdentifier::Mock(name.into()); let handle = PlatformFont::new_from_data(identifier.clone(), data.as_arc().clone(), 0, None) .expect("Could not load test font"); - let template = - FontTemplate::new_for_remote_web_font(url, handle.descriptor(), None).unwrap(); - family.add_template(template); + family.add_template(FontTemplate::new( + identifier.clone(), + handle.descriptor(), + None, + )); self.data.lock().insert(identifier, data); } } -impl SystemFontServiceProxyTrait for MockFontCacheThread { - fn find_matching_font_templates( - &self, - descriptor_to_match: Option<&FontDescriptor>, - font_family: &SingleFontFamily, - ) -> Vec { - self.find_font_count.fetch_add(1, Ordering::Relaxed); - - let SingleFontFamily::FamilyName(family_name) = font_family else { - return Vec::new(); - }; - - self.families - .lock() - .get_mut(&*family_name.name) - .map(|family| family.find_for_descriptor(descriptor_to_match)) - .unwrap_or_default() - } - - fn get_system_font_instance( - &self, - _font_identifier: FontIdentifier, - _size: Au, - _flags: FontInstanceFlags, - ) -> FontInstanceKey { - FontInstanceKey(IdNamespace(0), 0) - } - - fn get_web_font(&self, _data: Arc, _index: u32) -> FontKey { - FontKey(IdNamespace(0), 0) - } - - fn get_web_font_instance( - &self, - _font_key: FontKey, - _size: f32, - _flags: FontInstanceFlags, - ) -> FontInstanceKey { - FontInstanceKey(IdNamespace(0), 0) - } - - fn get_font_data(&self, identifier: &FontIdentifier) -> Option> { - self.data.lock().get(identifier).cloned() - } -} - fn style() -> FontStyleStruct { let mut style = FontStyleStruct { font_family: FontFamily::serif(), @@ -185,16 +225,9 @@ 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 = Arc::new(MockFontCacheThread::new()); - let context = FontContext::new(source, mock_resource_threads()); + let context = TestContext::new(); let style1 = style(); @@ -203,16 +236,28 @@ fn test_font_group_is_cached_by_style() { assert!( std::ptr::eq( - &*context.font_group(ServoArc::new(style1.clone())).read(), - &*context.font_group(ServoArc::new(style1.clone())).read() + &*context + .context + .font_group(ServoArc::new(style1.clone())) + .read(), + &*context + .context + .font_group(ServoArc::new(style1.clone())) + .read() ), "the same font group should be returned for two styles with the same hash" ); assert!( !std::ptr::eq( - &*context.font_group(ServoArc::new(style1.clone())).read(), - &*context.font_group(ServoArc::new(style2.clone())).read() + &*context + .context + .font_group(ServoArc::new(style1.clone())) + .read(), + &*context + .context + .font_group(ServoArc::new(style2.clone())) + .read() ), "different font groups should be returned for two styles with different hashes" ) @@ -220,52 +265,60 @@ fn test_font_group_is_cached_by_style() { #[test] fn test_font_group_find_by_codepoint() { - let source = Arc::new(MockFontCacheThread::new()); - let mut context = FontContext::new(source.clone(), mock_resource_threads()); + let mut context = TestContext::new(); let mut style = style(); style.set_font_family(font_family(vec!["CSSTest ASCII", "CSSTest Basic"])); - let group = context.font_group(ServoArc::new(style)); + let group = context.context.font_group(ServoArc::new(style)); let font = group .write() - .find_by_codepoint(&mut context, 'a', None) + .find_by_codepoint(&mut context.context, 'a', None) .unwrap(); assert_eq!( font.identifier(), - MockFontCacheThread::identifier_for_font_name("csstest-ascii") + FontIdentifier::Mock("csstest-ascii".into()) ); assert_eq!( - source.find_font_count.fetch_add(0, Ordering::Relaxed), + context + .system_font_service + .find_font_count + .fetch_add(0, Ordering::Relaxed), 1, "only the first font in the list should have been loaded" ); let font = group .write() - .find_by_codepoint(&mut context, 'a', None) + .find_by_codepoint(&mut context.context, 'a', None) .unwrap(); assert_eq!( font.identifier(), - MockFontCacheThread::identifier_for_font_name("csstest-ascii") + FontIdentifier::Mock("csstest-ascii".into()) ); assert_eq!( - source.find_font_count.fetch_add(0, Ordering::Relaxed), + context + .system_font_service + .find_font_count + .fetch_add(0, Ordering::Relaxed), 1, "we shouldn't load the same font a second time" ); let font = group .write() - .find_by_codepoint(&mut context, 'á', None) + .find_by_codepoint(&mut context.context, 'á', None) .unwrap(); assert_eq!( font.identifier(), - MockFontCacheThread::identifier_for_font_name("csstest-basic-regular") + FontIdentifier::Mock("csstest-basic-regular".into()) ); assert_eq!( - source.find_font_count.fetch_add(0, Ordering::Relaxed), + context + .system_font_service + .find_font_count + .fetch_add(0, Ordering::Relaxed), 2, "both fonts should now have been loaded" ); @@ -273,39 +326,37 @@ fn test_font_group_find_by_codepoint() { #[test] fn test_font_fallback() { - let source = Arc::new(MockFontCacheThread::new()); - let mut context = FontContext::new(source, mock_resource_threads()); + let mut context = TestContext::new(); let mut style = style(); style.set_font_family(font_family(vec!["CSSTest ASCII"])); - let group = context.font_group(ServoArc::new(style)); + let group = context.context.font_group(ServoArc::new(style)); let font = group .write() - .find_by_codepoint(&mut context, 'a', None) + .find_by_codepoint(&mut context.context, 'a', None) .unwrap(); assert_eq!( font.identifier(), - MockFontCacheThread::identifier_for_font_name("csstest-ascii"), + FontIdentifier::Mock("csstest-ascii".into()), "a family in the group should be used if there is a matching glyph" ); let font = group .write() - .find_by_codepoint(&mut context, 'á', None) + .find_by_codepoint(&mut context.context, 'á', None) .unwrap(); assert_eq!( font.identifier(), - MockFontCacheThread::identifier_for_font_name("csstest-basic-regular"), + FontIdentifier::Mock("csstest-basic-regular".into()), "a fallback font should be used if there is no matching glyph in the group" ); } #[test] fn test_font_template_is_cached() { - let source = Arc::new(MockFontCacheThread::new()); - let context = FontContext::new(source.clone(), mock_resource_threads()); + let context = TestContext::new(); let mut font_descriptor = FontDescriptor { weight: FontWeight::normal(), @@ -321,14 +372,19 @@ fn test_font_template_is_cached() { }); let family_descriptor = FontFamilyDescriptor::new(family, FontSearchScope::Any); - let font_template = context.matching_templates(&font_descriptor, &family_descriptor)[0].clone(); + let font_template = context + .context + .matching_templates(&font_descriptor, &family_descriptor)[0] + .clone(); let font1 = context + .context .font(font_template.clone(), &font_descriptor) .unwrap(); font_descriptor.pt_size = Au(20); let font2 = context + .context .font(font_template.clone(), &font_descriptor) .unwrap(); @@ -338,7 +394,10 @@ fn test_font_template_is_cached() { ); assert_eq!( - source.find_font_count.fetch_add(0, Ordering::Relaxed), + context + .system_font_service + .find_font_count + .fetch_add(0, Ordering::Relaxed), 1, "we should only have fetched the template data from the cache thread once" ); diff --git a/components/layout/context.rs b/components/layout/context.rs index 7cb4934d5a1..8272dbd1d18 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -11,7 +11,7 @@ use std::thread; use base::id::PipelineId; use fnv::FnvHasher; -use fonts::{FontContext, SystemFontServiceProxy}; +use fonts::FontContext; use net_traits::image_cache::{ ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder, }; @@ -24,8 +24,6 @@ use style::context::{RegisteredSpeculativePainter, SharedStyleContext}; use crate::display_list::items::{OpaqueNode, WebRenderImageInfo}; -pub type LayoutFontContext = FontContext; - type WebrenderImageCache = HashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo, BuildHasherDefault>; @@ -44,7 +42,7 @@ pub struct LayoutContext<'a> { pub image_cache: Arc, /// A FontContext to be used during layout. - pub font_context: Arc>, + pub font_context: Arc, /// A cache of WebRender image info. pub webrender_image_cache: Arc>, diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 6c984b9e40f..53540f1bc54 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -11,7 +11,7 @@ use app_units::{Au, MIN_AU}; use base::print_tree::PrintTree; use bitflags::bitflags; use euclid::default::{Point2D, Rect, Size2D}; -use fonts::FontMetrics; +use fonts::{FontContext, FontMetrics}; use log::debug; use range::{int_range_index, Range, RangeIndex}; use script_layout_interface::wrapper_traits::PseudoElementType; @@ -33,7 +33,7 @@ use style::values::specified::text::TextOverflowSide; use unicode_bidi as bidi; use crate::block::AbsoluteAssignBSizesTraversal; -use crate::context::{LayoutContext, LayoutFontContext}; +use crate::context::LayoutContext; use crate::display_list::items::{DisplayListSection, OpaqueNode}; use crate::display_list::{ BorderPaintingMode, DisplayListBuildState, StackingContextCollectionState, @@ -1239,7 +1239,7 @@ impl InlineFlow { /// `style` is the style of the block. pub fn minimum_line_metrics( &self, - font_context: &LayoutFontContext, + font_context: &FontContext, style: &ComputedValues, ) -> LineMetrics { InlineFlow::minimum_line_metrics_for_fragments( @@ -1255,7 +1255,7 @@ impl InlineFlow { /// `style` is the style of the block that these fragments belong to. pub fn minimum_line_metrics_for_fragments( fragments: &[Fragment], - font_context: &LayoutFontContext, + font_context: &FontContext, style: &ComputedValues, ) -> LineMetrics { // As a special case, if this flow contains only hypothetical fragments, then the entire diff --git a/components/layout/text.rs b/components/layout/text.rs index ec8901b93c8..fa75753a47d 100644 --- a/components/layout/text.rs +++ b/components/layout/text.rs @@ -11,7 +11,7 @@ use std::sync::Arc; use app_units::Au; use base::text::is_bidi_control; use fonts::{ - self, ByteIndex, FontIdentifier, FontMetrics, FontRef, RunMetrics, ShapingFlags, + self, ByteIndex, FontContext, FontIdentifier, FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions, LAST_RESORT_GLYPH_ADVANCE, }; use log::{debug, warn}; @@ -28,7 +28,6 @@ use unicode_bidi as bidi; use unicode_script::Script; use xi_unicode::LineBreakLeafIter; -use crate::context::LayoutFontContext; use crate::fragment::{ Fragment, ScannedTextFlags, ScannedTextFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo, @@ -71,7 +70,7 @@ impl TextRunScanner { pub fn scan_for_runs( &mut self, - font_context: &LayoutFontContext, + font_context: &FontContext, mut fragments: LinkedList, ) -> InlineFragments { debug!( @@ -151,7 +150,7 @@ impl TextRunScanner { /// be adjusted. fn flush_clump_to_list( &mut self, - font_context: &LayoutFontContext, + font_context: &FontContext, out_fragments: &mut Vec, paragraph_bytes_processed: &mut usize, bidi_levels: Option<&[bidi::Level]>, @@ -538,7 +537,7 @@ fn bounding_box_for_run_metrics( /// Panics if no font can be found for the given font style. #[inline] pub fn font_metrics_for_style( - font_context: &LayoutFontContext, + font_context: &FontContext, style: crate::ServoArc, ) -> FontMetrics { let font_group = font_context.font_group(style); diff --git a/components/layout_2020/context.rs b/components/layout_2020/context.rs index 352b31efd31..f1542327422 100644 --- a/components/layout_2020/context.rs +++ b/components/layout_2020/context.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use base::id::PipelineId; use fnv::FnvHashMap; -use fonts::{FontContext, SystemFontServiceProxy}; +use fonts::FontContext; use net_traits::image_cache::{ ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder, }; @@ -27,7 +27,7 @@ pub struct LayoutContext<'a> { pub style_context: SharedStyleContext<'a>, /// A FontContext to be used during layout. - pub font_context: Arc>, + pub font_context: Arc, /// Reference to the script thread image cache. pub image_cache: Arc, diff --git a/components/layout_2020/flow/inline/text_run.rs b/components/layout_2020/flow/inline/text_run.rs index 0b647d35097..d5438a103e1 100644 --- a/components/layout_2020/flow/inline/text_run.rs +++ b/components/layout_2020/flow/inline/text_run.rs @@ -8,8 +8,7 @@ use std::ops::Range; use app_units::Au; use base::text::is_bidi_control; use fonts::{ - FontContext, FontRef, GlyphRun, ShapingFlags, ShapingOptions, SystemFontServiceProxy, - LAST_RESORT_GLYPH_ADVANCE, + FontContext, FontRef, GlyphRun, ShapingFlags, ShapingOptions, LAST_RESORT_GLYPH_ADVANCE, }; use fonts_traits::ByteIndex; use log::warn; @@ -342,7 +341,7 @@ impl TextRun { pub(super) fn segment_and_shape( &mut self, formatting_context_text: &str, - font_context: &FontContext, + font_context: &FontContext, linebreaker: &mut LineBreaker, font_cache: &mut Vec, bidi_info: &BidiInfo, @@ -410,7 +409,7 @@ impl TextRun { fn segment_text_by_font( &mut self, formatting_context_text: &str, - font_context: &FontContext, + font_context: &FontContext, font_cache: &mut Vec, bidi_info: &BidiInfo, ) -> Vec<(TextRunSegment, FontRef)> { @@ -556,7 +555,7 @@ pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec, + font_context: &FontContext, ) -> Option { let font = font_context .font_group(style.clone_font()) diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 6100463eed9..75c1c331afc 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -134,7 +134,7 @@ pub struct LayoutThread { image_cache: Arc, /// A per-layout FontContext managing font access. - font_context: Arc>, + font_context: Arc, /// Is this the first reflow in this layout? first_reflow: Cell, @@ -577,7 +577,11 @@ impl LayoutThread { keyword_info: KeywordInfo::medium(), }; - let font_context = Arc::new(FontContext::new(system_font_service, resource_threads)); + let font_context = Arc::new(FontContext::new( + system_font_service, + webrender_api.clone(), + resource_threads, + )); let device = Device::new( MediaType::screen(), QuirksMode::NoQuirks, diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index de8f48886e0..070ad845fdb 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -121,7 +121,7 @@ pub struct LayoutThread { image_cache: Arc, /// A FontContext to be used during layout. - font_context: Arc>, + font_context: Arc, /// Is this the first reflow in this LayoutThread? first_reflow: Cell, @@ -521,7 +521,11 @@ 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(system_font_service, resource_threads)); + let font_context = Arc::new(FontContext::new( + system_font_service, + webrender_api_sender.clone(), + resource_threads, + )); let device = Device::new( MediaType::screen(), QuirksMode::NoQuirks, @@ -1227,7 +1231,7 @@ impl RegisteredSpeculativePainters for RegisteredPaintersImpl { } } -struct LayoutFontMetricsProvider(Arc>); +struct LayoutFontMetricsProvider(Arc); impl FontMetricsProvider for LayoutFontMetricsProvider { fn query_font_metrics( diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 15438de42fe..7c44981600d 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -1101,59 +1101,33 @@ impl WebRenderFontApi for WebRenderFontApiCompositorProxy { flags: FontInstanceFlags, ) -> FontInstanceKey { let (sender, receiver) = unbounded(); - self.0 - .send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font( - FontToCompositorMsg::AddFontInstance(font_key, size, flags, sender), - ))); + self.0.send(CompositorMsg::Forwarded( + ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFontInstance( + font_key, size, flags, sender, + )), + )); receiver.recv().unwrap() } fn add_font(&self, data: Arc, index: u32) -> FontKey { let (sender, receiver) = unbounded(); - self.0 - .send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font( - FontToCompositorMsg::AddFont(sender, index, data), - ))); + self.0.send(CompositorMsg::Forwarded( + ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFont( + sender, index, data, + )), + )); receiver.recv().unwrap() } fn add_system_font(&self, handle: NativeFontHandle) -> FontKey { let (sender, receiver) = unbounded(); - self.0 - .send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font( - FontToCompositorMsg::AddSystemFont(sender, handle), - ))); + self.0.send(CompositorMsg::Forwarded( + ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddSystemFont( + sender, handle, + )), + )); receiver.recv().unwrap() } - - fn forward_add_font_message( - &self, - data: Arc, - font_index: u32, - result_sender: IpcSender, - ) { - let (sender, receiver) = unbounded(); - self.0 - .send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font( - FontToCompositorMsg::AddFont(sender, font_index, data), - ))); - let _ = result_sender.send(receiver.recv().unwrap()); - } - - fn forward_add_font_instance_message( - &self, - font_key: FontKey, - size: f32, - flags: FontInstanceFlags, - result_sender: IpcSender, - ) { - 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/compositing/lib.rs b/components/shared/compositing/lib.rs index ccf24d0aabe..66d2199930e 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -141,7 +141,7 @@ pub struct CompositionPipeline { pub enum ForwardedToCompositorMsg { Layout(ScriptToCompositorMsg), Net(NetToCompositorMsg), - Font(FontToCompositorMsg), + SystemFontService(FontToCompositorMsg), Canvas(CanvasToCompositorMsg), } @@ -150,7 +150,9 @@ impl Debug for ForwardedToCompositorMsg { match self { ForwardedToCompositorMsg::Layout(_) => write!(f, "Layout(ScriptToCompositorMsg)"), ForwardedToCompositorMsg::Net(_) => write!(f, "Net(NetToCompositorMsg)"), - ForwardedToCompositorMsg::Font(_) => write!(f, "Font(FontToCompositorMsg)"), + ForwardedToCompositorMsg::SystemFontService(_) => { + write!(f, "SystemFontService(FontToCompositorMsg)") + }, ForwardedToCompositorMsg::Canvas(_) => write!(f, "Canvas(CanvasToCompositorMsg)"), } } diff --git a/components/shared/webrender/lib.rs b/components/shared/webrender/lib.rs index 14f3d3d338a..accb73df316 100644 --- a/components/shared/webrender/lib.rs +++ b/components/shared/webrender/lib.rs @@ -192,24 +192,6 @@ pub trait WebRenderFontApi { ) -> FontInstanceKey; fn add_font(&self, data: Arc, index: u32) -> FontKey; fn add_system_font(&self, handle: NativeFontHandle) -> FontKey; - - /// Forward a `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, - data: Arc, - font_index: u32, - result_sender: IpcSender, - ); - /// Forward a `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, - ); } pub enum CanvasToCompositorMsg { @@ -260,6 +242,8 @@ pub enum ScriptToCompositorMsg { UpdateImages(Vec), /// Remove the given font resources from our WebRender instance. RemoveFonts(Vec, Vec), + AddFontInstance(FontKey, f32, FontInstanceFlags, IpcSender), + AddFont(Arc, u32, IpcSender), } /// A mechanism to send messages from networking to the WebRender instance. @@ -294,11 +278,23 @@ impl WebRenderNetApi { pub struct WebRenderScriptApi(IpcSender); impl WebRenderScriptApi { - /// Create a new WebrenderIpcSender object that wraps the provided channel sender. + /// Create a new [`WebRenderScriptApi`] object that wraps the provided channel sender. pub fn new(sender: IpcSender) -> Self { Self(sender) } + /// Create a new [`WebRenderScriptApi`] object that does not have a listener on the + /// other end. + pub fn dummy() -> Self { + let (sender, _) = ipc::channel().unwrap(); + Self::new(sender) + } + + /// Get the sender for this proxy. + pub fn sender(&self) -> &IpcSender { + &self.0 + } + /// Inform WebRender of the existence of this pipeline. pub fn send_initial_transaction(&self, pipeline: WebRenderPipelineId) { if let Err(e) = self