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 <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-08-26 08:56:50 -07:00 committed by GitHub
parent 909ceee830
commit 4f68508624
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 281 additions and 193 deletions

4
Cargo.lock generated
View file

@ -1530,9 +1530,11 @@ version = "0.0.1"
dependencies = [ dependencies = [
"base", "base",
"canvas_traits", "canvas_traits",
"compositing_traits",
"devtools_traits", "devtools_traits",
"embedder_traits", "embedder_traits",
"euclid", "euclid",
"fonts_traits",
"http 1.3.1", "http 1.3.1",
"hyper_serde", "hyper_serde",
"ipc-channel", "ipc-channel",
@ -2720,6 +2722,8 @@ dependencies = [
"log", "log",
"malloc_size_of_derive", "malloc_size_of_derive",
"memmap2", "memmap2",
"parking_lot",
"profile_traits",
"range", "range",
"read-fonts", "read-fonts",
"serde", "serde",

View file

@ -2443,9 +2443,11 @@ where
let sw_senders = SWManagerSenders { let sw_senders = SWManagerSenders {
swmanager_sender: self.swmanager_ipc_sender.clone(), 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(), own_sender: own_sender.clone(),
receiver, 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 { if opts::get().multiprocess {

View file

@ -11,17 +11,15 @@ use app_units::Au;
use compositing_traits::CrossProcessCompositorApi; use compositing_traits::CrossProcessCompositorApi;
use fonts_traits::{ use fonts_traits::{
FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, LowercaseFontFamilyName, FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, LowercaseFontFamilyName,
SystemFontServiceMessage, SystemFontServiceProxySender,
}; };
use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::ipc::{self, IpcReceiver};
use log::debug;
use malloc_size_of::MallocSizeOf as MallocSizeOfTrait; use malloc_size_of::MallocSizeOf as MallocSizeOfTrait;
use malloc_size_of_derive::MallocSizeOf; use malloc_size_of_derive::MallocSizeOf;
use parking_lot::{Mutex, RwLock};
use profile_traits::mem::{ use profile_traits::mem::{
ProcessReports, ProfilerChan, Report, ReportKind, ReportsChan, perform_memory_report, ProcessReports, ProfilerChan, Report, ReportKind, ReportsChan, perform_memory_report,
}; };
use profile_traits::path; use profile_traits::path;
use serde::{Deserialize, Serialize};
use servo_config::pref; use servo_config::pref;
use style::values::computed::font::{GenericFontFamily, SingleFontFamily}; use style::values::computed::font::{GenericFontFamily, SingleFontFamily};
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation}; 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, 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<FontDescriptor>,
SingleFontFamily,
IpcSender<Vec<FontTemplate>>,
),
GetFontInstance(
FontIdentifier,
Au,
FontInstanceFlags,
Vec<FontVariation>,
IpcSender<FontInstanceKey>,
),
GetFontKey(IpcSender<FontKey>),
GetFontInstanceKey(IpcSender<FontInstanceKey>),
CollectMemoryReport(ReportsChan),
Exit(IpcSender<()>),
Ping,
}
#[derive(Default, MallocSizeOf)] #[derive(Default, MallocSizeOf)]
struct ResolvedGenericFontFamilies { struct ResolvedGenericFontFamilies {
default: OnceCell<LowercaseFontFamilyName>, default: OnceCell<LowercaseFontFamilyName>,
@ -89,18 +65,6 @@ pub struct SystemFontService {
free_font_instance_keys: Vec<FontInstanceKey>, free_font_instance_keys: Vec<FontInstanceKey>,
} }
#[derive(Clone, Deserialize, Serialize)]
pub struct SystemFontServiceProxySender(pub IpcSender<SystemFontServiceMessage>);
impl SystemFontServiceProxySender {
pub fn to_proxy(&self) -> SystemFontServiceProxy {
SystemFontServiceProxy {
sender: Mutex::new(self.0.clone()),
templates: Default::default(),
}
}
}
impl SystemFontService { impl SystemFontService {
pub fn spawn( pub fn spawn(
compositor_api: CrossProcessCompositorApi, compositor_api: CrossProcessCompositorApi,
@ -342,140 +306,3 @@ impl SystemFontService {
.clone() .clone()
} }
} }
#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
struct FontTemplateCacheKey {
font_descriptor: Option<FontDescriptor>,
family_descriptor: SingleFontFamily,
}
/// The public interface to the [`SystemFontService`], used by per-Document
/// `FontContext` instances.
#[derive(Debug, MallocSizeOf)]
pub struct SystemFontServiceProxy {
sender: Mutex<IpcSender<SystemFontServiceMessage>>,
templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
}
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<FontVariation>,
) -> 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<FontTemplateRef> {
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.")
}
}

View file

@ -87,6 +87,7 @@ impl DebuggerGlobalScope {
gpu_id_hub, gpu_id_hub,
None, None,
false, false,
None, // font_context
), ),
devtools_to_script_sender, devtools_to_script_sender,
get_possible_breakpoints_result_sender: RefCell::new(None), get_possible_breakpoints_result_sender: RefCell::new(None),

View file

@ -11,6 +11,7 @@ use constellation_traits::{WorkerGlobalScopeInit, WorkerScriptLoadOrigin};
use crossbeam_channel::{Receiver, Sender, unbounded}; use crossbeam_channel::{Receiver, Sender, unbounded};
use devtools_traits::DevtoolScriptControlMsg; use devtools_traits::DevtoolScriptControlMsg;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use fonts::FontContext;
use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader}; use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
use ipc_channel::ipc::IpcReceiver; use ipc_channel::ipc::IpcReceiver;
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
@ -283,6 +284,7 @@ impl DedicatedWorkerGlobalScope {
#[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>, #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
control_receiver: Receiver<DedicatedWorkerControlMsg>, control_receiver: Receiver<DedicatedWorkerControlMsg>,
insecure_requests_policy: InsecureRequestsPolicy, insecure_requests_policy: InsecureRequestsPolicy,
font_context: Option<Arc<FontContext>>,
) -> DedicatedWorkerGlobalScope { ) -> DedicatedWorkerGlobalScope {
DedicatedWorkerGlobalScope { DedicatedWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited( workerglobalscope: WorkerGlobalScope::new_inherited(
@ -296,6 +298,7 @@ impl DedicatedWorkerGlobalScope {
#[cfg(feature = "webgpu")] #[cfg(feature = "webgpu")]
gpu_id_hub, gpu_id_hub,
insecure_requests_policy, insecure_requests_policy,
font_context,
), ),
task_queue: TaskQueue::new(receiver, own_sender.clone()), task_queue: TaskQueue::new(receiver, own_sender.clone()),
own_sender, own_sender,
@ -324,6 +327,7 @@ impl DedicatedWorkerGlobalScope {
#[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>, #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
control_receiver: Receiver<DedicatedWorkerControlMsg>, control_receiver: Receiver<DedicatedWorkerControlMsg>,
insecure_requests_policy: InsecureRequestsPolicy, insecure_requests_policy: InsecureRequestsPolicy,
font_context: Option<Arc<FontContext>>,
) -> DomRoot<DedicatedWorkerGlobalScope> { ) -> DomRoot<DedicatedWorkerGlobalScope> {
let scope = Box::new(DedicatedWorkerGlobalScope::new_inherited( let scope = Box::new(DedicatedWorkerGlobalScope::new_inherited(
init, init,
@ -342,6 +346,7 @@ impl DedicatedWorkerGlobalScope {
gpu_id_hub, gpu_id_hub,
control_receiver, control_receiver,
insecure_requests_policy, insecure_requests_policy,
font_context,
)); ));
DedicatedWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>( DedicatedWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(
GlobalScope::get_cx(), GlobalScope::get_cx(),
@ -370,6 +375,7 @@ impl DedicatedWorkerGlobalScope {
context_sender: Sender<ThreadSafeJSContext>, context_sender: Sender<ThreadSafeJSContext>,
insecure_requests_policy: InsecureRequestsPolicy, insecure_requests_policy: InsecureRequestsPolicy,
policy_container: PolicyContainer, policy_container: PolicyContainer,
font_context: Option<Arc<FontContext>>,
) -> JoinHandle<()> { ) -> JoinHandle<()> {
let serialized_worker_url = worker_url.to_string(); let serialized_worker_url = worker_url.to_string();
let webview_id = WebViewId::installed(); let webview_id = WebViewId::installed();
@ -485,6 +491,7 @@ impl DedicatedWorkerGlobalScope {
gpu_id_hub, gpu_id_hub,
control_receiver, control_receiver,
insecure_requests_policy, insecure_requests_policy,
font_context,
); );
debugger_global.fire_add_debuggee( debugger_global.fire_add_debuggee(
CanGc::note(), CanGc::note(),

View file

@ -69,6 +69,7 @@ impl DissimilarOriginWindow {
global_to_clone_from.wgpu_id_hub(), global_to_clone_from.wgpu_id_hub(),
Some(global_to_clone_from.is_secure_context()), Some(global_to_clone_from.is_secure_context()),
false, false,
global_to_clone_from.font_context().cloned(),
), ),
window_proxy: Dom::from_ref(window_proxy), window_proxy: Dom::from_ref(window_proxy),
location: Default::default(), location: Default::default(),

View file

@ -27,6 +27,7 @@ use crossbeam_channel::Sender;
use devtools_traits::{PageError, ScriptToDevtoolsControlMsg}; use devtools_traits::{PageError, ScriptToDevtoolsControlMsg};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use embedder_traits::{EmbedderMsg, JavaScriptEvaluationError}; use embedder_traits::{EmbedderMsg, JavaScriptEvaluationError};
use fonts::FontContext;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use js::glue::{IsWrapper, UnwrapObjectDynamic}; use js::glue::{IsWrapper, UnwrapObjectDynamic};
@ -383,6 +384,13 @@ pub(crate) struct GlobalScope {
/// <https://html.spec.whatwg.org/multipage/#resolved-module-set> /// <https://html.spec.whatwg.org/multipage/#resolved-module-set>
resolved_module_set: DomRefCell<HashSet<ResolvedModule>>, resolved_module_set: DomRefCell<HashSet<ResolvedModule>>,
/// 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<Arc<FontContext>>,
} }
/// A wrapper for glue-code between the ipc router and the event-loop. /// 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<IdentityHub>, #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
inherited_secure_context: Option<bool>, inherited_secure_context: Option<bool>,
unminify_js: bool, unminify_js: bool,
font_context: Option<Arc<FontContext>>,
) -> Self { ) -> Self {
Self { Self {
task_manager: Default::default(), task_manager: Default::default(),
@ -789,6 +798,7 @@ impl GlobalScope {
notification_permission_request_callback_map: Default::default(), notification_permission_request_callback_map: Default::default(),
import_map: Default::default(), import_map: Default::default(),
resolved_module_set: Default::default(), resolved_module_set: Default::default(),
font_context,
} }
} }
@ -815,6 +825,10 @@ impl GlobalScope {
self.timers.get_or_init(|| OneshotTimers::new(self)) self.timers.get_or_init(|| OneshotTimers::new(self))
} }
pub(crate) fn font_context(&self) -> Option<&Arc<FontContext>> {
self.font_context.as_ref()
}
/// <https://w3c.github.io/ServiceWorker/#get-the-service-worker-registration-object> /// <https://w3c.github.io/ServiceWorker/#get-the-service-worker-registration-object>
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(crate) fn get_serviceworker_registration( pub(crate) fn get_serviceworker_registration(

View file

@ -14,6 +14,7 @@ use constellation_traits::{
use crossbeam_channel::{Receiver, Sender, after, unbounded}; use crossbeam_channel::{Receiver, Sender, after, unbounded};
use devtools_traits::DevtoolScriptControlMsg; use devtools_traits::DevtoolScriptControlMsg;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use fonts::FontContext;
use ipc_channel::ipc::{IpcReceiver, IpcSender}; use ipc_channel::ipc::{IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use js::jsapi::{JS_AddInterruptCallback, JSContext}; use js::jsapi::{JS_AddInterruptCallback, JSContext};
@ -227,6 +228,7 @@ impl ServiceWorkerGlobalScope {
scope_url: ServoUrl, scope_url: ServoUrl,
control_receiver: Receiver<ServiceWorkerControlMsg>, control_receiver: Receiver<ServiceWorkerControlMsg>,
closing: Arc<AtomicBool>, closing: Arc<AtomicBool>,
font_context: Arc<FontContext>,
) -> ServiceWorkerGlobalScope { ) -> ServiceWorkerGlobalScope {
ServiceWorkerGlobalScope { ServiceWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited( workerglobalscope: WorkerGlobalScope::new_inherited(
@ -239,8 +241,9 @@ impl ServiceWorkerGlobalScope {
closing, closing,
#[cfg(feature = "webgpu")] #[cfg(feature = "webgpu")]
Arc::new(IdentityHub::default()), Arc::new(IdentityHub::default()),
InsecureRequestsPolicy::DoNotUpgrade, // FIXME: investigate what environment this value comes from for // FIXME: investigate what environment this value comes from for service workers.
// service workers. InsecureRequestsPolicy::DoNotUpgrade,
Some(font_context),
), ),
task_queue: TaskQueue::new(receiver, own_sender.clone()), task_queue: TaskQueue::new(receiver, own_sender.clone()),
own_sender, own_sender,
@ -264,6 +267,7 @@ impl ServiceWorkerGlobalScope {
scope_url: ServoUrl, scope_url: ServoUrl,
control_receiver: Receiver<ServiceWorkerControlMsg>, control_receiver: Receiver<ServiceWorkerControlMsg>,
closing: Arc<AtomicBool>, closing: Arc<AtomicBool>,
font_context: Arc<FontContext>,
) -> DomRoot<ServiceWorkerGlobalScope> { ) -> DomRoot<ServiceWorkerGlobalScope> {
let scope = Box::new(ServiceWorkerGlobalScope::new_inherited( let scope = Box::new(ServiceWorkerGlobalScope::new_inherited(
init, init,
@ -277,6 +281,7 @@ impl ServiceWorkerGlobalScope {
scope_url, scope_url,
control_receiver, control_receiver,
closing, closing,
font_context,
)); ));
ServiceWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(GlobalScope::get_cx(), scope) ServiceWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(GlobalScope::get_cx(), scope)
} }
@ -293,6 +298,7 @@ impl ServiceWorkerGlobalScope {
control_receiver: Receiver<ServiceWorkerControlMsg>, control_receiver: Receiver<ServiceWorkerControlMsg>,
context_sender: Sender<ThreadSafeJSContext>, context_sender: Sender<ThreadSafeJSContext>,
closing: Arc<AtomicBool>, closing: Arc<AtomicBool>,
font_context: Arc<FontContext>,
) -> JoinHandle<()> { ) -> JoinHandle<()> {
let ScopeThings { let ScopeThings {
script_url, script_url,
@ -342,6 +348,7 @@ impl ServiceWorkerGlobalScope {
scope_url, scope_url,
control_receiver, control_receiver,
closing, closing,
font_context,
); );
let scope = global.upcast::<WorkerGlobalScope>(); let scope = global.upcast::<WorkerGlobalScope>();

View file

@ -246,11 +246,6 @@ pub(crate) struct Window {
#[no_trace] #[no_trace]
#[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"] #[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"]
layout: RefCell<Box<dyn Layout>>, layout: RefCell<Box<dyn Layout>>,
/// 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<FontContext>,
navigator: MutNullableDom<Navigator>, navigator: MutNullableDom<Navigator>,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
@ -722,7 +717,9 @@ impl Window {
} }
pub(crate) fn font_context(&self) -> &Arc<FontContext> { pub(crate) fn font_context(&self) -> &Arc<FontContext> {
&self.font_context self.as_global_scope()
.font_context()
.expect("A `Window` should always have a `FontContext`")
} }
pub(crate) fn ongoing_navigation(&self) -> OngoingNavigation { pub(crate) fn ongoing_navigation(&self) -> OngoingNavigation {
@ -2275,7 +2272,7 @@ impl Window {
return; return;
} }
if self.font_context.web_fonts_still_loading() != 0 { if self.font_context().web_fonts_still_loading() != 0 {
return; return;
} }
@ -3136,11 +3133,11 @@ impl Window {
gpu_id_hub, gpu_id_hub,
inherited_secure_context, inherited_secure_context,
unminify_js, unminify_js,
Some(font_context.clone()),
), ),
ongoing_navigation: Default::default(), ongoing_navigation: Default::default(),
script_chan, script_chan,
layout: RefCell::new(layout), layout: RefCell::new(layout),
font_context,
image_cache_sender, image_cache_sender,
image_cache, image_cache,
navigator: Default::default(), navigator: Default::default(),

View file

@ -255,6 +255,7 @@ impl WorkerMethods<crate::DomTypeHolder> for Worker {
context_sender, context_sender,
global.insecure_requests_policy(), global.insecure_requests_policy(),
global.policy_container(), global.policy_container(),
global.font_context().cloned(),
); );
let context = context_receiver let context = context_receiver

View file

@ -16,6 +16,7 @@ use content_security_policy::CspList;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use devtools_traits::{DevtoolScriptControlMsg, WorkerId}; use devtools_traits::{DevtoolScriptControlMsg, WorkerId};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use fonts::FontContext;
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use js::jsval::UndefinedValue; use js::jsval::UndefinedValue;
use js::panic::maybe_resume_unwind; use js::panic::maybe_resume_unwind;
@ -167,6 +168,7 @@ impl WorkerGlobalScope {
closing: Arc<AtomicBool>, closing: Arc<AtomicBool>,
#[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>, #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
insecure_requests_policy: InsecureRequestsPolicy, insecure_requests_policy: InsecureRequestsPolicy,
font_context: Option<Arc<FontContext>>,
) -> Self { ) -> Self {
// Install a pipeline-namespace in the current thread. // Install a pipeline-namespace in the current thread.
PipelineNamespace::auto_install(); PipelineNamespace::auto_install();
@ -192,6 +194,7 @@ impl WorkerGlobalScope {
gpu_id_hub, gpu_id_hub,
init.inherited_secure_context, init.inherited_secure_context,
false, false,
font_context,
), ),
worker_id: init.worker_id, worker_id: init.worker_id,
worker_name, worker_name,

View file

@ -108,6 +108,7 @@ impl WorkletGlobalScope {
init.gpu_id_hub.clone(), init.gpu_id_hub.clone(),
init.inherited_secure_context, init.inherited_secure_context,
false, false,
None, // font_context
), ),
base_url, base_url,
to_script_thread_sender: init.to_script_thread_sender.clone(), to_script_thread_sender: init.to_script_thread_sender.clone(),

View file

@ -18,6 +18,7 @@ use constellation_traits::{
ScopeThings, ServiceWorkerManagerFactory, ServiceWorkerMsg, ScopeThings, ServiceWorkerManagerFactory, ServiceWorkerMsg,
}; };
use crossbeam_channel::{Receiver, RecvError, Sender, select, unbounded}; use crossbeam_channel::{Receiver, RecvError, Sender, select, unbounded};
use fonts::FontContext;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use net_traits::{CoreResourceMsg, CustomResponseMediator}; use net_traits::{CoreResourceMsg, CustomResponseMediator};
@ -223,6 +224,8 @@ pub struct ServiceWorkerManager {
own_port: Receiver<ServiceWorkerMsg>, own_port: Receiver<ServiceWorkerMsg>,
// to receive resource messages // to receive resource messages
resource_receiver: Receiver<CustomResponseMediator>, resource_receiver: Receiver<CustomResponseMediator>,
/// A shared [`FontContext`] to use for all service workers spawned by this [`ServiceWorkerManager`].
font_context: Arc<FontContext>,
} }
impl ServiceWorkerManager { impl ServiceWorkerManager {
@ -231,6 +234,7 @@ impl ServiceWorkerManager {
from_constellation_receiver: Receiver<ServiceWorkerMsg>, from_constellation_receiver: Receiver<ServiceWorkerMsg>,
resource_port: Receiver<CustomResponseMediator>, resource_port: Receiver<CustomResponseMediator>,
constellation_sender: IpcSender<SWManagerMsg>, constellation_sender: IpcSender<SWManagerMsg>,
font_context: Arc<FontContext>,
) -> ServiceWorkerManager { ) -> ServiceWorkerManager {
// Install a pipeline-namespace in the current thread. // Install a pipeline-namespace in the current thread.
PipelineNamespace::auto_install(); PipelineNamespace::auto_install();
@ -241,6 +245,7 @@ impl ServiceWorkerManager {
own_port: from_constellation_receiver, own_port: from_constellation_receiver,
resource_receiver: resource_port, resource_receiver: resource_port,
_constellation_sender: constellation_sender, _constellation_sender: constellation_sender,
font_context,
} }
} }
@ -406,8 +411,12 @@ impl ServiceWorkerManager {
// Very roughly steps 5 to 18. // Very roughly steps 5 to 18.
// TODO: implement all steps precisely. // TODO: implement all steps precisely.
let (new_worker, join_handle, control_sender, context, closing) = let (new_worker, join_handle, control_sender, context, closing) = update_serviceworker(
update_serviceworker(self.own_sender.clone(), job.scope_url.clone(), scope_things); 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. // 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); registration.note_worker_thread(join_handle, control_sender, context, closing);
@ -446,6 +455,7 @@ fn update_serviceworker(
own_sender: IpcSender<ServiceWorkerMsg>, own_sender: IpcSender<ServiceWorkerMsg>,
scope_url: ServoUrl, scope_url: ServoUrl,
scope_things: ScopeThings, scope_things: ScopeThings,
font_context: Arc<FontContext>,
) -> ( ) -> (
ServiceWorker, ServiceWorker,
JoinHandle<()>, JoinHandle<()>,
@ -471,6 +481,7 @@ fn update_serviceworker(
control_receiver, control_receiver,
context_sender, context_sender,
closing.clone(), closing.clone(),
font_context,
); );
let context = context_receiver let context = context_receiver
@ -491,21 +502,33 @@ impl ServiceWorkerManagerFactory for ServiceWorkerManager {
let (resource_chan, resource_port) = ipc::channel().unwrap(); let (resource_chan, resource_port) = ipc::channel().unwrap();
let SWManagerSenders { let SWManagerSenders {
resource_sender, resource_threads,
own_sender, own_sender,
receiver, receiver,
swmanager_sender: constellation_sender, swmanager_sender: constellation_sender,
system_font_service_sender,
compositor_api,
} = sw_senders; } = sw_senders;
let from_constellation = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(receiver); 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_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 || { let swmanager_thread = move || {
ServiceWorkerManager::new( ServiceWorkerManager::new(
own_sender, own_sender,
from_constellation, from_constellation,
resource_port, resource_port,
constellation_sender, constellation_sender,
font_context,
) )
.handle_message() .handle_message()
}; };

View file

@ -18,9 +18,11 @@ webgpu = ["wgpu-core"]
[dependencies] [dependencies]
base = { workspace = true } base = { workspace = true }
canvas_traits = { workspace = true } canvas_traits = { workspace = true }
compositing_traits = { workspace = true }
devtools_traits = { workspace = true } devtools_traits = { workspace = true }
embedder_traits = { workspace = true } embedder_traits = { workspace = true }
euclid = { workspace = true } euclid = { workspace = true }
fonts_traits = { workspace = true }
http = { workspace = true } http = { workspace = true }
hyper_serde = { workspace = true } hyper_serde = { workspace = true }
ipc-channel = { workspace = true } ipc-channel = { workspace = true }

View file

@ -13,6 +13,7 @@ use base::id::{
MessagePortRouterId, PipelineId, ServiceWorkerId, ServiceWorkerRegistrationId, WebViewId, MessagePortRouterId, PipelineId, ServiceWorkerId, ServiceWorkerRegistrationId, WebViewId,
}; };
use canvas_traits::canvas::{CanvasId, CanvasMsg}; use canvas_traits::canvas::{CanvasId, CanvasMsg};
use compositing_traits::CrossProcessCompositorApi;
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
use embedder_traits::{ use embedder_traits::{
AnimationState, EmbedderMsg, FocusSequenceNumber, JSValue, JavaScriptEvaluationError, AnimationState, EmbedderMsg, FocusSequenceNumber, JSValue, JavaScriptEvaluationError,
@ -20,6 +21,7 @@ use embedder_traits::{
WebDriverMessageId, WebDriverMessageId,
}; };
use euclid::default::Size2D as UntypedSize2D; use euclid::default::Size2D as UntypedSize2D;
use fonts_traits::SystemFontServiceProxySender;
use http::{HeaderMap, Method}; use http::{HeaderMap, Method};
use ipc_channel::Error as IpcError; use ipc_channel::Error as IpcError;
use ipc_channel::ipc::{IpcReceiver, IpcSender}; 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::policy_container::PolicyContainer;
use net_traits::request::{Destination, InsecureRequestsPolicy, Referrer, RequestBody}; use net_traits::request::{Destination, InsecureRequestsPolicy, Referrer, RequestBody};
use net_traits::storage_thread::StorageType; 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::MemoryReportResult;
use profile_traits::{mem, time as profile_time}; use profile_traits::{mem, time as profile_time};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -205,8 +207,12 @@ pub struct DOMMessage {
pub struct SWManagerSenders { pub struct SWManagerSenders {
/// Sender of messages to the constellation. /// Sender of messages to the constellation.
pub swmanager_sender: IpcSender<SWManagerMsg>, pub swmanager_sender: IpcSender<SWManagerMsg>,
/// Sender for communicating with resource thread. /// [`ResourceThreads`] for initating fetches or using i/o.
pub resource_sender: IpcSender<CoreResourceMsg>, 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. /// Sender of messages to the manager.
pub own_sender: IpcSender<ServiceWorkerMsg>, pub own_sender: IpcSender<ServiceWorkerMsg>,
/// Receiver of messages from the constellation. /// Receiver of messages from the constellation.

View file

@ -18,6 +18,8 @@ log = { workspace = true }
malloc_size_of = { workspace = true } malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true } malloc_size_of_derive = { workspace = true }
memmap2 = { workspace = true } memmap2 = { workspace = true }
parking_lot = { workspace = true }
profile_traits = { workspace = true }
range = { path = "../../range" } range = { path = "../../range" }
read-fonts = { workspace = true } read-fonts = { workspace = true }
serde = { workspace = true } serde = { workspace = true }

View file

@ -7,6 +7,7 @@
mod font_descriptor; mod font_descriptor;
mod font_identifier; mod font_identifier;
mod font_template; mod font_template;
mod system_font_service_proxy;
use std::sync::Arc; use std::sync::Arc;
@ -17,6 +18,7 @@ use ipc_channel::ipc::IpcSharedMemory;
use malloc_size_of_derive::MallocSizeOf; use malloc_size_of_derive::MallocSizeOf;
use range::{RangeIndex, int_range_index}; use range::{RangeIndex, int_range_index};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub use system_font_service_proxy::*;
int_range_index! { int_range_index! {
#[derive(Deserialize, MallocSizeOf, Serialize)] #[derive(Deserialize, MallocSizeOf, Serialize)]

View file

@ -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<FontDescriptor>,
SingleFontFamily,
IpcSender<Vec<FontTemplate>>,
),
GetFontInstance(
FontIdentifier,
Au,
FontInstanceFlags,
Vec<FontVariation>,
IpcSender<FontInstanceKey>,
),
GetFontKey(IpcSender<FontKey>),
GetFontInstanceKey(IpcSender<FontInstanceKey>),
CollectMemoryReport(ReportsChan),
Exit(IpcSender<()>),
Ping,
}
#[derive(Clone, Deserialize, Serialize)]
pub struct SystemFontServiceProxySender(pub IpcSender<SystemFontServiceMessage>);
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<FontDescriptor>,
family_descriptor: SingleFontFamily,
}
/// The public interface to the [`SystemFontService`], used by per-Document
/// `FontContext` instances.
#[derive(Debug, MallocSizeOf)]
pub struct SystemFontServiceProxy {
sender: Mutex<IpcSender<SystemFontServiceMessage>>,
templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
}
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<FontVariation>,
) -> 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<FontTemplateRef> {
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.")
}
}