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

View file

@ -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),

View file

@ -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<IdentityHub>,
control_receiver: Receiver<DedicatedWorkerControlMsg>,
insecure_requests_policy: InsecureRequestsPolicy,
font_context: Option<Arc<FontContext>>,
) -> 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<IdentityHub>,
control_receiver: Receiver<DedicatedWorkerControlMsg>,
insecure_requests_policy: InsecureRequestsPolicy,
font_context: Option<Arc<FontContext>>,
) -> DomRoot<DedicatedWorkerGlobalScope> {
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::<crate::DomTypeHolder>(
GlobalScope::get_cx(),
@ -370,6 +375,7 @@ impl DedicatedWorkerGlobalScope {
context_sender: Sender<ThreadSafeJSContext>,
insecure_requests_policy: InsecureRequestsPolicy,
policy_container: PolicyContainer,
font_context: Option<Arc<FontContext>>,
) -> 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(),

View file

@ -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(),

View file

@ -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 {
/// <https://html.spec.whatwg.org/multipage/#resolved-module-set>
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.
@ -741,6 +749,7 @@ impl GlobalScope {
#[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
inherited_secure_context: Option<bool>,
unminify_js: bool,
font_context: Option<Arc<FontContext>>,
) -> 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<FontContext>> {
self.font_context.as_ref()
}
/// <https://w3c.github.io/ServiceWorker/#get-the-service-worker-registration-object>
#[allow(clippy::too_many_arguments)]
pub(crate) fn get_serviceworker_registration(

View file

@ -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<ServiceWorkerControlMsg>,
closing: Arc<AtomicBool>,
font_context: Arc<FontContext>,
) -> 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<ServiceWorkerControlMsg>,
closing: Arc<AtomicBool>,
font_context: Arc<FontContext>,
) -> DomRoot<ServiceWorkerGlobalScope> {
let scope = Box::new(ServiceWorkerGlobalScope::new_inherited(
init,
@ -277,6 +281,7 @@ impl ServiceWorkerGlobalScope {
scope_url,
control_receiver,
closing,
font_context,
));
ServiceWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(GlobalScope::get_cx(), scope)
}
@ -293,6 +298,7 @@ impl ServiceWorkerGlobalScope {
control_receiver: Receiver<ServiceWorkerControlMsg>,
context_sender: Sender<ThreadSafeJSContext>,
closing: Arc<AtomicBool>,
font_context: Arc<FontContext>,
) -> JoinHandle<()> {
let ScopeThings {
script_url,
@ -342,6 +348,7 @@ impl ServiceWorkerGlobalScope {
scope_url,
control_receiver,
closing,
font_context,
);
let scope = global.upcast::<WorkerGlobalScope>();

View file

@ -246,11 +246,6 @@ pub(crate) struct Window {
#[no_trace]
#[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to 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>,
#[ignore_malloc_size_of = "Arc"]
#[no_trace]
@ -722,7 +717,9 @@ impl Window {
}
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 {
@ -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(),

View file

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

View file

@ -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<AtomicBool>,
#[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
insecure_requests_policy: InsecureRequestsPolicy,
font_context: Option<Arc<FontContext>>,
) -> 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,

View file

@ -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(),