From 4f685086242dbbc7fc819a9376a5ca06923e1135 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Tue, 26 Aug 2025 08:56:50 -0700 Subject: [PATCH] script: Move `FontContext` from `Window` to `GlobalScope` (#38918) This change makes it so that every `GlobalScope` can contain an optional `FontContext`. This will be necessary for every `GlobalScope` that accesses canvas. Currently, `FontContext` is created and accessed via the canvas worker thread, but this means that web fonts are not available to canvas. This change will eventually make it possible for canvas to share web fonts with the `Document` that owns them. Testing: This should not change behavior and is thus covered by existing tests. Signed-off-by: Martin Robinson --- Cargo.lock | 4 + components/constellation/constellation.rs | 4 +- components/fonts/system_font_service.rs | 177 +---------------- components/script/dom/debuggerglobalscope.rs | 1 + .../script/dom/dedicatedworkerglobalscope.rs | 7 + .../script/dom/dissimilaroriginwindow.rs | 1 + components/script/dom/globalscope.rs | 14 ++ .../script/dom/serviceworkerglobalscope.rs | 11 +- components/script/dom/window.rs | 13 +- components/script/dom/worker.rs | 1 + components/script/dom/workerglobalscope.rs | 3 + components/script/dom/workletglobalscope.rs | 1 + components/script/serviceworker_manager.rs | 31 ++- components/shared/constellation/Cargo.toml | 2 + .../constellation/from_script_message.rs | 12 +- components/shared/fonts/Cargo.toml | 2 + components/shared/fonts/lib.rs | 2 + .../shared/fonts/system_font_service_proxy.rs | 188 ++++++++++++++++++ 18 files changed, 281 insertions(+), 193 deletions(-) create mode 100644 components/shared/fonts/system_font_service_proxy.rs diff --git a/Cargo.lock b/Cargo.lock index 32b8d709533..11466398098 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1530,9 +1530,11 @@ version = "0.0.1" dependencies = [ "base", "canvas_traits", + "compositing_traits", "devtools_traits", "embedder_traits", "euclid", + "fonts_traits", "http 1.3.1", "hyper_serde", "ipc-channel", @@ -2720,6 +2722,8 @@ dependencies = [ "log", "malloc_size_of_derive", "memmap2", + "parking_lot", + "profile_traits", "range", "read-fonts", "serde", diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 0b5358805dd..958d163fec4 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -2443,9 +2443,11 @@ where let sw_senders = SWManagerSenders { swmanager_sender: self.swmanager_ipc_sender.clone(), - resource_sender: self.public_resource_threads.sender(), + resource_threads: self.public_resource_threads.clone(), own_sender: own_sender.clone(), receiver, + compositor_api: self.compositor_proxy.cross_process_compositor_api.clone(), + system_font_service_sender: self.system_font_service.to_sender(), }; if opts::get().multiprocess { diff --git a/components/fonts/system_font_service.rs b/components/fonts/system_font_service.rs index 8f88d761648..eaaa0bb52db 100644 --- a/components/fonts/system_font_service.rs +++ b/components/fonts/system_font_service.rs @@ -11,17 +11,15 @@ use app_units::Au; use compositing_traits::CrossProcessCompositorApi; use fonts_traits::{ FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, LowercaseFontFamilyName, + SystemFontServiceMessage, SystemFontServiceProxySender, }; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; -use log::debug; +use ipc_channel::ipc::{self, IpcReceiver}; use malloc_size_of::MallocSizeOf as MallocSizeOfTrait; use malloc_size_of_derive::MallocSizeOf; -use parking_lot::{Mutex, RwLock}; use profile_traits::mem::{ ProcessReports, ProfilerChan, Report, ReportKind, ReportsChan, perform_memory_report, }; use profile_traits::path; -use serde::{Deserialize, Serialize}; use servo_config::pref; use style::values::computed::font::{GenericFontFamily, SingleFontFamily}; use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation}; @@ -31,28 +29,6 @@ use crate::platform::font_list::{ default_system_generic_font_family, for_each_available_family, for_each_variation, }; -/// Commands that the `FontContext` sends to the `SystemFontService`. -#[derive(Debug, Deserialize, Serialize)] -pub enum SystemFontServiceMessage { - GetFontTemplates( - Option, - SingleFontFamily, - IpcSender>, - ), - GetFontInstance( - FontIdentifier, - Au, - FontInstanceFlags, - Vec, - IpcSender, - ), - GetFontKey(IpcSender), - GetFontInstanceKey(IpcSender), - CollectMemoryReport(ReportsChan), - Exit(IpcSender<()>), - Ping, -} - #[derive(Default, MallocSizeOf)] struct ResolvedGenericFontFamilies { default: OnceCell, @@ -89,18 +65,6 @@ pub struct SystemFontService { free_font_instance_keys: Vec, } -#[derive(Clone, Deserialize, Serialize)] -pub struct SystemFontServiceProxySender(pub IpcSender); - -impl SystemFontServiceProxySender { - pub fn to_proxy(&self) -> SystemFontServiceProxy { - SystemFontServiceProxy { - sender: Mutex::new(self.0.clone()), - templates: Default::default(), - } - } -} - impl SystemFontService { pub fn spawn( compositor_api: CrossProcessCompositorApi, @@ -342,140 +306,3 @@ impl SystemFontService { .clone() } } - -#[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. -#[derive(Debug, MallocSizeOf)] -pub struct SystemFontServiceProxy { - sender: Mutex>, - templates: RwLock>>, -} - -impl SystemFontServiceProxy { - pub fn exit(&self) { - let (response_chan, response_port) = ipc::channel().unwrap(); - self.sender - .lock() - .send(SystemFontServiceMessage::Exit(response_chan)) - .expect("Couldn't send SystemFontService exit message"); - response_port - .recv() - .expect("Couldn't receive SystemFontService reply"); - } - - pub fn to_sender(&self) -> SystemFontServiceProxySender { - SystemFontServiceProxySender(self.sender.lock().clone()) - } - - pub(crate) fn get_system_font_instance( - &self, - identifier: FontIdentifier, - size: Au, - flags: FontInstanceFlags, - variations: Vec, - ) -> FontInstanceKey { - let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); - self.sender - .lock() - .send(SystemFontServiceMessage::GetFontInstance( - identifier, - size, - flags, - variations, - response_chan, - )) - .expect("failed to send message to system font service"); - - let instance_key = response_port.recv(); - if instance_key.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" - ); - panic!("SystemFontService has already exited."); - } - instance_key.unwrap() - } - - pub(crate) fn find_matching_font_templates( - &self, - descriptor_to_match: Option<&FontDescriptor>, - family_descriptor: &SingleFontFamily, - ) -> Vec { - let cache_key = FontTemplateCacheKey { - font_descriptor: descriptor_to_match.cloned(), - family_descriptor: family_descriptor.clone(), - }; - if let Some(templates) = self.templates.read().get(&cache_key).cloned() { - return templates; - } - - debug!( - "SystemFontServiceProxy: cache miss for template_descriptor={:?} family_descriptor={:?}", - descriptor_to_match, family_descriptor - ); - - let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); - self.sender - .lock() - .send(SystemFontServiceMessage::GetFontTemplates( - descriptor_to_match.cloned(), - family_descriptor.clone(), - response_chan, - )) - .expect("failed to send message to system font service"); - - let Ok(templates) = response_port.recv() else { - 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" - ); - panic!("SystemFontService has already exited."); - }; - - let templates: Vec<_> = templates.into_iter().map(FontTemplateRef::new).collect(); - self.templates.write().insert(cache_key, templates.clone()); - - templates - } - - pub(crate) fn generate_font_key(&self) -> FontKey { - let (result_sender, result_receiver) = - ipc::channel().expect("failed to create IPC channel"); - self.sender - .lock() - .send(SystemFontServiceMessage::GetFontKey(result_sender)) - .expect("failed to send message to system font service"); - result_receiver - .recv() - .expect("Failed to communicate with system font service.") - } - - pub(crate) fn generate_font_instance_key(&self) -> FontInstanceKey { - let (result_sender, result_receiver) = - ipc::channel().expect("failed to create IPC channel"); - self.sender - .lock() - .send(SystemFontServiceMessage::GetFontInstanceKey(result_sender)) - .expect("failed to send message to system font service"); - result_receiver - .recv() - .expect("Failed to communicate with system font service.") - } -} diff --git a/components/script/dom/debuggerglobalscope.rs b/components/script/dom/debuggerglobalscope.rs index 3833b24889b..ca5c3090b06 100644 --- a/components/script/dom/debuggerglobalscope.rs +++ b/components/script/dom/debuggerglobalscope.rs @@ -87,6 +87,7 @@ impl DebuggerGlobalScope { gpu_id_hub, None, false, + None, // font_context ), devtools_to_script_sender, get_possible_breakpoints_result_sender: RefCell::new(None), diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 423d33d9f5c..5c85312413a 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -11,6 +11,7 @@ use constellation_traits::{WorkerGlobalScopeInit, WorkerScriptLoadOrigin}; use crossbeam_channel::{Receiver, Sender, unbounded}; use devtools_traits::DevtoolScriptControlMsg; use dom_struct::dom_struct; +use fonts::FontContext; use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader}; use ipc_channel::ipc::IpcReceiver; use ipc_channel::router::ROUTER; @@ -283,6 +284,7 @@ impl DedicatedWorkerGlobalScope { #[cfg(feature = "webgpu")] gpu_id_hub: Arc, control_receiver: Receiver, insecure_requests_policy: InsecureRequestsPolicy, + font_context: Option>, ) -> DedicatedWorkerGlobalScope { DedicatedWorkerGlobalScope { workerglobalscope: WorkerGlobalScope::new_inherited( @@ -296,6 +298,7 @@ impl DedicatedWorkerGlobalScope { #[cfg(feature = "webgpu")] gpu_id_hub, insecure_requests_policy, + font_context, ), task_queue: TaskQueue::new(receiver, own_sender.clone()), own_sender, @@ -324,6 +327,7 @@ impl DedicatedWorkerGlobalScope { #[cfg(feature = "webgpu")] gpu_id_hub: Arc, control_receiver: Receiver, insecure_requests_policy: InsecureRequestsPolicy, + font_context: Option>, ) -> DomRoot { let scope = Box::new(DedicatedWorkerGlobalScope::new_inherited( init, @@ -342,6 +346,7 @@ impl DedicatedWorkerGlobalScope { gpu_id_hub, control_receiver, insecure_requests_policy, + font_context, )); DedicatedWorkerGlobalScopeBinding::Wrap::( GlobalScope::get_cx(), @@ -370,6 +375,7 @@ impl DedicatedWorkerGlobalScope { context_sender: Sender, insecure_requests_policy: InsecureRequestsPolicy, policy_container: PolicyContainer, + font_context: Option>, ) -> JoinHandle<()> { let serialized_worker_url = worker_url.to_string(); let webview_id = WebViewId::installed(); @@ -485,6 +491,7 @@ impl DedicatedWorkerGlobalScope { gpu_id_hub, control_receiver, insecure_requests_policy, + font_context, ); debugger_global.fire_add_debuggee( CanGc::note(), diff --git a/components/script/dom/dissimilaroriginwindow.rs b/components/script/dom/dissimilaroriginwindow.rs index 23ebd2e90f6..7988f226563 100644 --- a/components/script/dom/dissimilaroriginwindow.rs +++ b/components/script/dom/dissimilaroriginwindow.rs @@ -69,6 +69,7 @@ impl DissimilarOriginWindow { global_to_clone_from.wgpu_id_hub(), Some(global_to_clone_from.is_secure_context()), false, + global_to_clone_from.font_context().cloned(), ), window_proxy: Dom::from_ref(window_proxy), location: Default::default(), diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 3d6d05f65c7..a5ddceeee38 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -27,6 +27,7 @@ use crossbeam_channel::Sender; use devtools_traits::{PageError, ScriptToDevtoolsControlMsg}; use dom_struct::dom_struct; use embedder_traits::{EmbedderMsg, JavaScriptEvaluationError}; +use fonts::FontContext; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use js::glue::{IsWrapper, UnwrapObjectDynamic}; @@ -383,6 +384,13 @@ pub(crate) struct GlobalScope { /// resolved_module_set: DomRefCell>, + + /// The [`FontContext`] for this [`GlobalScope`] if it has one. This is used for + /// canvas and layout, so if this [`GlobalScope`] doesn't need to use either, this + /// might be `None`. + #[conditional_malloc_size_of] + #[no_trace] + font_context: Option>, } /// A wrapper for glue-code between the ipc router and the event-loop. @@ -741,6 +749,7 @@ impl GlobalScope { #[cfg(feature = "webgpu")] gpu_id_hub: Arc, inherited_secure_context: Option, unminify_js: bool, + font_context: Option>, ) -> Self { Self { task_manager: Default::default(), @@ -789,6 +798,7 @@ impl GlobalScope { notification_permission_request_callback_map: Default::default(), import_map: Default::default(), resolved_module_set: Default::default(), + font_context, } } @@ -815,6 +825,10 @@ impl GlobalScope { self.timers.get_or_init(|| OneshotTimers::new(self)) } + pub(crate) fn font_context(&self) -> Option<&Arc> { + self.font_context.as_ref() + } + /// #[allow(clippy::too_many_arguments)] pub(crate) fn get_serviceworker_registration( diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index 51cf1d7a07c..283740d87da 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -14,6 +14,7 @@ use constellation_traits::{ use crossbeam_channel::{Receiver, Sender, after, unbounded}; use devtools_traits::DevtoolScriptControlMsg; use dom_struct::dom_struct; +use fonts::FontContext; use ipc_channel::ipc::{IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use js::jsapi::{JS_AddInterruptCallback, JSContext}; @@ -227,6 +228,7 @@ impl ServiceWorkerGlobalScope { scope_url: ServoUrl, control_receiver: Receiver, closing: Arc, + font_context: Arc, ) -> ServiceWorkerGlobalScope { ServiceWorkerGlobalScope { workerglobalscope: WorkerGlobalScope::new_inherited( @@ -239,8 +241,9 @@ impl ServiceWorkerGlobalScope { closing, #[cfg(feature = "webgpu")] Arc::new(IdentityHub::default()), - InsecureRequestsPolicy::DoNotUpgrade, // FIXME: investigate what environment this value comes from for - // service workers. + // FIXME: investigate what environment this value comes from for service workers. + InsecureRequestsPolicy::DoNotUpgrade, + Some(font_context), ), task_queue: TaskQueue::new(receiver, own_sender.clone()), own_sender, @@ -264,6 +267,7 @@ impl ServiceWorkerGlobalScope { scope_url: ServoUrl, control_receiver: Receiver, closing: Arc, + font_context: Arc, ) -> DomRoot { let scope = Box::new(ServiceWorkerGlobalScope::new_inherited( init, @@ -277,6 +281,7 @@ impl ServiceWorkerGlobalScope { scope_url, control_receiver, closing, + font_context, )); ServiceWorkerGlobalScopeBinding::Wrap::(GlobalScope::get_cx(), scope) } @@ -293,6 +298,7 @@ impl ServiceWorkerGlobalScope { control_receiver: Receiver, context_sender: Sender, closing: Arc, + font_context: Arc, ) -> JoinHandle<()> { let ScopeThings { script_url, @@ -342,6 +348,7 @@ impl ServiceWorkerGlobalScope { scope_url, control_receiver, closing, + font_context, ); let scope = global.upcast::(); diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 9adb5a022bc..49464f46ad7 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -246,11 +246,6 @@ pub(crate) struct Window { #[no_trace] #[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"] layout: RefCell>, - /// A [`FontContext`] which is used to store and match against fonts for this `Window` and to - /// trigger the download of web fonts. - #[no_trace] - #[conditional_malloc_size_of] - font_context: Arc, navigator: MutNullableDom, #[ignore_malloc_size_of = "Arc"] #[no_trace] @@ -722,7 +717,9 @@ impl Window { } pub(crate) fn font_context(&self) -> &Arc { - &self.font_context + self.as_global_scope() + .font_context() + .expect("A `Window` should always have a `FontContext`") } pub(crate) fn ongoing_navigation(&self) -> OngoingNavigation { @@ -2275,7 +2272,7 @@ impl Window { return; } - if self.font_context.web_fonts_still_loading() != 0 { + if self.font_context().web_fonts_still_loading() != 0 { return; } @@ -3136,11 +3133,11 @@ impl Window { gpu_id_hub, inherited_secure_context, unminify_js, + Some(font_context.clone()), ), ongoing_navigation: Default::default(), script_chan, layout: RefCell::new(layout), - font_context, image_cache_sender, image_cache, navigator: Default::default(), diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs index 9f555283a82..ce95e834e39 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -255,6 +255,7 @@ impl WorkerMethods for Worker { context_sender, global.insecure_requests_policy(), global.policy_container(), + global.font_context().cloned(), ); let context = context_receiver diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 178b1166c97..6b212319be3 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -16,6 +16,7 @@ use content_security_policy::CspList; use crossbeam_channel::Receiver; use devtools_traits::{DevtoolScriptControlMsg, WorkerId}; use dom_struct::dom_struct; +use fonts::FontContext; use ipc_channel::ipc::IpcSender; use js::jsval::UndefinedValue; use js::panic::maybe_resume_unwind; @@ -167,6 +168,7 @@ impl WorkerGlobalScope { closing: Arc, #[cfg(feature = "webgpu")] gpu_id_hub: Arc, insecure_requests_policy: InsecureRequestsPolicy, + font_context: Option>, ) -> Self { // Install a pipeline-namespace in the current thread. PipelineNamespace::auto_install(); @@ -192,6 +194,7 @@ impl WorkerGlobalScope { gpu_id_hub, init.inherited_secure_context, false, + font_context, ), worker_id: init.worker_id, worker_name, diff --git a/components/script/dom/workletglobalscope.rs b/components/script/dom/workletglobalscope.rs index a083a38f1da..e4d35b2f17d 100644 --- a/components/script/dom/workletglobalscope.rs +++ b/components/script/dom/workletglobalscope.rs @@ -108,6 +108,7 @@ impl WorkletGlobalScope { init.gpu_id_hub.clone(), init.inherited_secure_context, false, + None, // font_context ), base_url, to_script_thread_sender: init.to_script_thread_sender.clone(), diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs index a2ac034f662..30a589641b1 100644 --- a/components/script/serviceworker_manager.rs +++ b/components/script/serviceworker_manager.rs @@ -18,6 +18,7 @@ use constellation_traits::{ ScopeThings, ServiceWorkerManagerFactory, ServiceWorkerMsg, }; use crossbeam_channel::{Receiver, RecvError, Sender, select, unbounded}; +use fonts::FontContext; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use net_traits::{CoreResourceMsg, CustomResponseMediator}; @@ -223,6 +224,8 @@ pub struct ServiceWorkerManager { own_port: Receiver, // to receive resource messages resource_receiver: Receiver, + /// A shared [`FontContext`] to use for all service workers spawned by this [`ServiceWorkerManager`]. + font_context: Arc, } impl ServiceWorkerManager { @@ -231,6 +234,7 @@ impl ServiceWorkerManager { from_constellation_receiver: Receiver, resource_port: Receiver, constellation_sender: IpcSender, + font_context: Arc, ) -> ServiceWorkerManager { // Install a pipeline-namespace in the current thread. PipelineNamespace::auto_install(); @@ -241,6 +245,7 @@ impl ServiceWorkerManager { own_port: from_constellation_receiver, resource_receiver: resource_port, _constellation_sender: constellation_sender, + font_context, } } @@ -406,8 +411,12 @@ impl ServiceWorkerManager { // Very roughly steps 5 to 18. // TODO: implement all steps precisely. - let (new_worker, join_handle, control_sender, context, closing) = - update_serviceworker(self.own_sender.clone(), job.scope_url.clone(), scope_things); + let (new_worker, join_handle, control_sender, context, closing) = update_serviceworker( + self.own_sender.clone(), + job.scope_url.clone(), + scope_things, + self.font_context.clone(), + ); // Since we've just started the worker thread, ensure we can shut it down later. registration.note_worker_thread(join_handle, control_sender, context, closing); @@ -446,6 +455,7 @@ fn update_serviceworker( own_sender: IpcSender, scope_url: ServoUrl, scope_things: ScopeThings, + font_context: Arc, ) -> ( ServiceWorker, JoinHandle<()>, @@ -471,6 +481,7 @@ fn update_serviceworker( control_receiver, context_sender, closing.clone(), + font_context, ); let context = context_receiver @@ -491,21 +502,33 @@ impl ServiceWorkerManagerFactory for ServiceWorkerManager { let (resource_chan, resource_port) = ipc::channel().unwrap(); let SWManagerSenders { - resource_sender, + resource_threads, own_sender, receiver, swmanager_sender: constellation_sender, + system_font_service_sender, + compositor_api, } = sw_senders; let from_constellation = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(receiver); let resource_port = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(resource_port); - let _ = resource_sender.send(CoreResourceMsg::NetworkMediator(resource_chan, origin)); + let _ = resource_threads + .core_thread + .send(CoreResourceMsg::NetworkMediator(resource_chan, origin)); + + let font_context = Arc::new(FontContext::new( + Arc::new(system_font_service_sender.to_proxy()), + compositor_api, + resource_threads, + )); + let swmanager_thread = move || { ServiceWorkerManager::new( own_sender, from_constellation, resource_port, constellation_sender, + font_context, ) .handle_message() }; diff --git a/components/shared/constellation/Cargo.toml b/components/shared/constellation/Cargo.toml index d3b1d25882b..beee88ba3c4 100644 --- a/components/shared/constellation/Cargo.toml +++ b/components/shared/constellation/Cargo.toml @@ -18,9 +18,11 @@ webgpu = ["wgpu-core"] [dependencies] base = { workspace = true } canvas_traits = { workspace = true } +compositing_traits = { workspace = true } devtools_traits = { workspace = true } embedder_traits = { workspace = true } euclid = { workspace = true } +fonts_traits = { workspace = true } http = { workspace = true } hyper_serde = { workspace = true } ipc-channel = { workspace = true } diff --git a/components/shared/constellation/from_script_message.rs b/components/shared/constellation/from_script_message.rs index 1f96d933a93..b71a23e2561 100644 --- a/components/shared/constellation/from_script_message.rs +++ b/components/shared/constellation/from_script_message.rs @@ -13,6 +13,7 @@ use base::id::{ MessagePortRouterId, PipelineId, ServiceWorkerId, ServiceWorkerRegistrationId, WebViewId, }; use canvas_traits::canvas::{CanvasId, CanvasMsg}; +use compositing_traits::CrossProcessCompositorApi; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; use embedder_traits::{ AnimationState, EmbedderMsg, FocusSequenceNumber, JSValue, JavaScriptEvaluationError, @@ -20,6 +21,7 @@ use embedder_traits::{ WebDriverMessageId, }; use euclid::default::Size2D as UntypedSize2D; +use fonts_traits::SystemFontServiceProxySender; use http::{HeaderMap, Method}; use ipc_channel::Error as IpcError; use ipc_channel::ipc::{IpcReceiver, IpcSender}; @@ -27,7 +29,7 @@ use malloc_size_of_derive::MallocSizeOf; use net_traits::policy_container::PolicyContainer; use net_traits::request::{Destination, InsecureRequestsPolicy, Referrer, RequestBody}; use net_traits::storage_thread::StorageType; -use net_traits::{CoreResourceMsg, ReferrerPolicy, ResourceThreads}; +use net_traits::{ReferrerPolicy, ResourceThreads}; use profile_traits::mem::MemoryReportResult; use profile_traits::{mem, time as profile_time}; use serde::{Deserialize, Serialize}; @@ -205,8 +207,12 @@ pub struct DOMMessage { pub struct SWManagerSenders { /// Sender of messages to the constellation. pub swmanager_sender: IpcSender, - /// Sender for communicating with resource thread. - pub resource_sender: IpcSender, + /// [`ResourceThreads`] for initating fetches or using i/o. + pub resource_threads: ResourceThreads, + /// [`CrossProcessCompositorApi`] for communicating with the compositor. + pub compositor_api: CrossProcessCompositorApi, + /// The [`SystemFontServiceProxy`] used to communicate with the `SystemFontService`. + pub system_font_service_sender: SystemFontServiceProxySender, /// Sender of messages to the manager. pub own_sender: IpcSender, /// Receiver of messages from the constellation. diff --git a/components/shared/fonts/Cargo.toml b/components/shared/fonts/Cargo.toml index 3e51d3ce200..be02bf7b7e9 100644 --- a/components/shared/fonts/Cargo.toml +++ b/components/shared/fonts/Cargo.toml @@ -18,6 +18,8 @@ log = { workspace = true } malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } memmap2 = { workspace = true } +parking_lot = { workspace = true } +profile_traits = { workspace = true } range = { path = "../../range" } read-fonts = { workspace = true } serde = { workspace = true } diff --git a/components/shared/fonts/lib.rs b/components/shared/fonts/lib.rs index 9370a3eddda..eb0b9cbee68 100644 --- a/components/shared/fonts/lib.rs +++ b/components/shared/fonts/lib.rs @@ -7,6 +7,7 @@ mod font_descriptor; mod font_identifier; mod font_template; +mod system_font_service_proxy; use std::sync::Arc; @@ -17,6 +18,7 @@ use ipc_channel::ipc::IpcSharedMemory; use malloc_size_of_derive::MallocSizeOf; use range::{RangeIndex, int_range_index}; use serde::{Deserialize, Serialize}; +pub use system_font_service_proxy::*; int_range_index! { #[derive(Deserialize, MallocSizeOf, Serialize)] diff --git a/components/shared/fonts/system_font_service_proxy.rs b/components/shared/fonts/system_font_service_proxy.rs new file mode 100644 index 00000000000..fa5a5e7c730 --- /dev/null +++ b/components/shared/fonts/system_font_service_proxy.rs @@ -0,0 +1,188 @@ +/* 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 ipc_channel::ipc::{self, IpcSender}; +use log::debug; +use malloc_size_of_derive::MallocSizeOf; +use parking_lot::{Mutex, RwLock}; +use profile_traits::mem::ReportsChan; +use serde::{Deserialize, Serialize}; +use style::values::computed::font::SingleFontFamily; +use webrender_api::units::Au; +use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation}; + +use crate::{FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef}; + +/// Commands that the `FontContext` sends to the `SystemFontService`. +#[derive(Debug, Deserialize, Serialize)] +pub enum SystemFontServiceMessage { + GetFontTemplates( + Option, + SingleFontFamily, + IpcSender>, + ), + GetFontInstance( + FontIdentifier, + Au, + FontInstanceFlags, + Vec, + IpcSender, + ), + GetFontKey(IpcSender), + GetFontInstanceKey(IpcSender), + CollectMemoryReport(ReportsChan), + Exit(IpcSender<()>), + Ping, +} + +#[derive(Clone, Deserialize, Serialize)] +pub struct SystemFontServiceProxySender(pub IpcSender); + +impl SystemFontServiceProxySender { + pub fn to_proxy(&self) -> SystemFontServiceProxy { + SystemFontServiceProxy { + sender: Mutex::new(self.0.clone()), + templates: Default::default(), + } + } +} + +#[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. +#[derive(Debug, MallocSizeOf)] +pub struct SystemFontServiceProxy { + sender: Mutex>, + templates: RwLock>>, +} + +impl SystemFontServiceProxy { + pub fn exit(&self) { + let (response_chan, response_port) = ipc::channel().unwrap(); + self.sender + .lock() + .send(SystemFontServiceMessage::Exit(response_chan)) + .expect("Couldn't send SystemFontService exit message"); + response_port + .recv() + .expect("Couldn't receive SystemFontService reply"); + } + + pub fn to_sender(&self) -> SystemFontServiceProxySender { + SystemFontServiceProxySender(self.sender.lock().clone()) + } + + pub fn get_system_font_instance( + &self, + identifier: FontIdentifier, + size: Au, + flags: FontInstanceFlags, + variations: Vec, + ) -> FontInstanceKey { + let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); + self.sender + .lock() + .send(SystemFontServiceMessage::GetFontInstance( + identifier, + size, + flags, + variations, + response_chan, + )) + .expect("failed to send message to system font service"); + + let instance_key = response_port.recv(); + if instance_key.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" + ); + panic!("SystemFontService has already exited."); + } + instance_key.unwrap() + } + + pub fn find_matching_font_templates( + &self, + descriptor_to_match: Option<&FontDescriptor>, + family_descriptor: &SingleFontFamily, + ) -> Vec { + let cache_key = FontTemplateCacheKey { + font_descriptor: descriptor_to_match.cloned(), + family_descriptor: family_descriptor.clone(), + }; + if let Some(templates) = self.templates.read().get(&cache_key).cloned() { + return templates; + } + + debug!( + "SystemFontServiceProxy: cache miss for template_descriptor={:?} family_descriptor={:?}", + descriptor_to_match, family_descriptor + ); + + let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); + self.sender + .lock() + .send(SystemFontServiceMessage::GetFontTemplates( + descriptor_to_match.cloned(), + family_descriptor.clone(), + response_chan, + )) + .expect("failed to send message to system font service"); + + let Ok(templates) = response_port.recv() else { + 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" + ); + panic!("SystemFontService has already exited."); + }; + + let templates: Vec<_> = templates.into_iter().map(FontTemplateRef::new).collect(); + self.templates.write().insert(cache_key, templates.clone()); + + templates + } + + pub fn generate_font_key(&self) -> FontKey { + let (result_sender, result_receiver) = + ipc::channel().expect("failed to create IPC channel"); + self.sender + .lock() + .send(SystemFontServiceMessage::GetFontKey(result_sender)) + .expect("failed to send message to system font service"); + result_receiver + .recv() + .expect("Failed to communicate with system font service.") + } + + pub fn generate_font_instance_key(&self) -> FontInstanceKey { + let (result_sender, result_receiver) = + ipc::channel().expect("failed to create IPC channel"); + self.sender + .lock() + .send(SystemFontServiceMessage::GetFontInstanceKey(result_sender)) + .expect("failed to send message to system font service"); + result_receiver + .recv() + .expect("Failed to communicate with system font service.") + } +}