fonts: Clean up messaging during web fonts loads (#32332)

Instead of sending a message to the script thread via IPC when a web
font loads and then sending another, just give the `FontContext` a
callback that send a single message to the script thread. This moves all
the cache invalidation internally into `FontContext` as well.

Additionally, the unused LayoutControlMessage::ExitNow enum variant is
removed.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
This commit is contained in:
Martin Robinson 2024-05-22 10:30:35 +02:00 committed by GitHub
parent d47c8ff2ae
commit 9f32809671
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 150 additions and 205 deletions

1
Cargo.lock generated
View file

@ -2022,6 +2022,7 @@ dependencies = [
"core-foundation", "core-foundation",
"core-graphics", "core-graphics",
"core-text", "core-text",
"crossbeam-channel",
"cssparser", "cssparser",
"dwrote", "dwrote",
"euclid", "euclid",

View file

@ -18,6 +18,7 @@ app_units = { workspace = true }
atomic_refcell = { workspace = true } atomic_refcell = { workspace = true }
bitflags = { workspace = true } bitflags = { workspace = true }
cssparser = { workspace = true } cssparser = { workspace = true }
crossbeam-channel = { workspace = true }
euclid = { workspace = true } euclid = { workspace = true }
fnv = { workspace = true } fnv = { workspace = true }
fontsan = { git = "https://github.com/servo/fontsan" } fontsan = { git = "https://github.com/servo/fontsan" }

View file

@ -5,11 +5,13 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::default::Default; use std::default::Default;
use std::hash::{BuildHasherDefault, Hash, Hasher}; use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use app_units::Au; use app_units::Au;
use crossbeam_channel::unbounded;
use fnv::FnvHasher; use fnv::FnvHasher;
use ipc_channel::ipc::{self, IpcSender}; use gfx_traits::WebFontLoadFinishedCallback;
use log::{debug, trace}; use log::{debug, trace};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use malloc_size_of_derive::MallocSizeOf; use malloc_size_of_derive::MallocSizeOf;
@ -49,6 +51,7 @@ pub struct FontContext<S: FontSource> {
cache: CachingFontSource<S>, cache: CachingFontSource<S>,
web_fonts: CrossThreadFontStore, web_fonts: CrossThreadFontStore,
webrender_font_store: CrossThreadWebRenderFontStore, webrender_font_store: CrossThreadWebRenderFontStore,
web_fonts_still_loading: AtomicUsize,
} }
impl<S: FontSource> MallocSizeOf for FontContext<S> { impl<S: FontSource> MallocSizeOf for FontContext<S> {
@ -66,15 +69,25 @@ impl<S: FontSource> FontContext<S> {
cache: CachingFontSource::new(font_source), cache: CachingFontSource::new(font_source),
web_fonts: Arc::new(RwLock::default()), web_fonts: Arc::new(RwLock::default()),
webrender_font_store: Arc::new(RwLock::default()), webrender_font_store: Arc::new(RwLock::default()),
web_fonts_still_loading: Default::default(),
} }
} }
/// Invalidate all caches that this [`FontContext`] holds and any in-process platform-specific pub fn web_fonts_still_loading(&self) -> usize {
/// caches. self.web_fonts_still_loading.load(Ordering::SeqCst)
pub fn invalidate_caches(&self) { }
#[cfg(target_os = "macos")]
CoreTextFontCache::clear_core_text_font_cache(); /// Handle the situation where a web font finishes loading, specifying if the load suceeded or failed.
self.cache.invalidate() fn handle_web_font_load_finished(
&self,
finished_callback: &WebFontLoadFinishedCallback,
succeeded: bool,
) {
self.web_fonts_still_loading.fetch_sub(1, Ordering::SeqCst);
if succeeded {
self.cache.invalidate_after_web_font_load();
}
finished_callback(succeeded);
} }
/// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`. /// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`.
@ -218,7 +231,7 @@ impl<S: FontSource> FontContext<S> {
pub struct WebFontDownloadState { pub struct WebFontDownloadState {
css_font_face_descriptors: Arc<CSSFontFaceDescriptors>, css_font_face_descriptors: Arc<CSSFontFaceDescriptors>,
remaining_sources: Vec<Source>, remaining_sources: Vec<Source>,
result_sender: IpcSender<()>, finished_callback: WebFontLoadFinishedCallback,
core_resource_thread: CoreResourceThread, core_resource_thread: CoreResourceThread,
local_fonts: Arc<HashMap<Atom, Option<FontTemplateRef>>>, local_fonts: Arc<HashMap<Atom, Option<FontTemplateRef>>>,
} }
@ -229,7 +242,7 @@ pub trait FontContextWebFontMethods {
stylesheet: &Stylesheet, stylesheet: &Stylesheet,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
device: &Device, device: &Device,
font_cache_sender: &IpcSender<()>, finished_callback: WebFontLoadFinishedCallback,
synchronous: bool, synchronous: bool,
) -> usize; ) -> usize;
fn process_next_web_font_source(&self, web_font_download_state: WebFontDownloadState); fn process_next_web_font_source(&self, web_font_download_state: WebFontDownloadState);
@ -241,14 +254,20 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte
stylesheet: &Stylesheet, stylesheet: &Stylesheet,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
device: &Device, device: &Device,
font_cache_sender: &IpcSender<()>, finished_callback: WebFontLoadFinishedCallback,
synchronous: bool, synchronous: bool,
) -> usize { ) -> usize {
let (result_sender, receiver) = if synchronous { let (finished_callback, synchronous_receiver) = if synchronous {
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = unbounded();
(Some(sender), Some(receiver)) let finished_callback = move |_succeeded: bool| {
let _ = sender.send(());
};
(
Arc::new(finished_callback) as WebFontLoadFinishedCallback,
Some(receiver),
)
} else { } else {
(None, None) (finished_callback, None)
}; };
let mut number_loading = 0; let mut number_loading = 0;
@ -293,20 +312,21 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte
} }
} }
let result_sender = result_sender.as_ref().unwrap_or(font_cache_sender).clone();
self.process_next_web_font_source(WebFontDownloadState { self.process_next_web_font_source(WebFontDownloadState {
css_font_face_descriptors: Arc::new(rule.into()), css_font_face_descriptors: Arc::new(rule.into()),
remaining_sources: sources, remaining_sources: sources,
result_sender, finished_callback: finished_callback.clone(),
core_resource_thread: self.resource_threads.lock().clone(), core_resource_thread: self.resource_threads.lock().clone(),
local_fonts: Arc::new(local_fonts), local_fonts: Arc::new(local_fonts),
}); });
// Either increment the count of loading web fonts, or wait for a synchronous load.
if let Some(ref receiver) = receiver {
receiver.recv().unwrap();
}
number_loading += 1; number_loading += 1;
self.web_fonts_still_loading.fetch_add(1, Ordering::SeqCst);
// If the load is synchronous wait for it to be signalled.
if let Some(ref synchronous_receiver) = synchronous_receiver {
synchronous_receiver.recv().unwrap();
}
}); });
number_loading number_loading
@ -314,7 +334,7 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte
fn process_next_web_font_source(&self, mut state: WebFontDownloadState) { fn process_next_web_font_source(&self, mut state: WebFontDownloadState) {
let Some(source) = state.remaining_sources.pop() else { let Some(source) = state.remaining_sources.pop() else {
state.result_sender.send(()).unwrap(); self.handle_web_font_load_finished(&state.finished_callback, false);
return; return;
}; };
@ -344,7 +364,7 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte
.entry(web_font_family_name.clone()) .entry(web_font_family_name.clone())
.or_default() .or_default()
.add_template(new_template); .add_template(new_template);
drop(state.result_sender.send(())); self.handle_web_font_load_finished(&state.finished_callback, true);
} else { } else {
this.process_next_web_font_source(state); this.process_next_web_font_source(state);
} }
@ -452,8 +472,8 @@ impl<FCT: FontSource + Send + 'static> RemoteWebFontDownloader<FCT> {
.or_default() .or_default()
.add_template(new_template); .add_template(new_template);
// Signal the Document that we have finished trying to load this web font. self.font_context
drop(state.result_sender.send(())); .handle_web_font_load_finished(&state.finished_callback, true);
true true
} }
@ -519,9 +539,7 @@ impl<FCT: FontSource> CachingFontSource<FCT> {
} }
} }
fn invalidate(&self) { fn invalidate_after_web_font_load(&self) {
self.fonts.write().clear();
self.templates.write().clear();
self.resolved_font_groups.write().clear(); self.resolved_font_groups.write().clear();
} }

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use base::id::PipelineId; use base::id::PipelineId;
use fnv::FnvHashMap; use fnv::FnvHashMap;
@ -11,7 +11,7 @@ use gfx::font_context::FontContext;
use net_traits::image_cache::{ use net_traits::image_cache::{
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder, ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
}; };
use parking_lot::RwLock; use parking_lot::{Mutex, RwLock};
use script_layout_interface::{PendingImage, PendingImageState}; use script_layout_interface::{PendingImage, PendingImageState};
use servo_url::{ImmutableOrigin, ServoUrl}; use servo_url::{ImmutableOrigin, ServoUrl};
use style::context::SharedStyleContext; use style::context::SharedStyleContext;
@ -43,7 +43,7 @@ pub struct LayoutContext<'a> {
impl<'a> Drop for LayoutContext<'a> { impl<'a> Drop for LayoutContext<'a> {
fn drop(&mut self) { fn drop(&mut self) {
if !std::thread::panicking() { if !std::thread::panicking() {
assert!(self.pending_images.lock().unwrap().is_empty()); assert!(self.pending_images.lock().is_empty());
} }
} }
} }
@ -79,7 +79,7 @@ impl<'a> LayoutContext<'a> {
id, id,
origin: self.origin.clone(), origin: self.origin.clone(),
}; };
self.pending_images.lock().unwrap().push(image); self.pending_images.lock().push(image);
None None
}, },
// Not yet requested - request image or metadata from the cache // Not yet requested - request image or metadata from the cache
@ -90,7 +90,7 @@ impl<'a> LayoutContext<'a> {
id, id,
origin: self.origin.clone(), origin: self.origin.clone(),
}; };
self.pending_images.lock().unwrap().push(image); self.pending_images.lock().push(image);
None None
}, },
// Image failed to load, so just return nothing // Image failed to load, so just return nothing

View file

@ -13,7 +13,6 @@ use std::cell::{Cell, RefCell};
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::process; use std::process;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use app_units::Au; use app_units::Au;
@ -27,9 +26,9 @@ use fxhash::{FxHashMap, FxHashSet};
use gfx::font; use gfx::font;
use gfx::font_cache_thread::FontCacheThread; use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::{FontContext, FontContextWebFontMethods}; use gfx::font_context::{FontContext, FontContextWebFontMethods};
use gfx_traits::WebFontLoadFinishedCallback;
use histogram::Histogram; use histogram::Histogram;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::IpcSender;
use ipc_channel::router::ROUTER;
use layout::construct::ConstructionResult; use layout::construct::ConstructionResult;
use layout::context::{LayoutContext, RegisteredPainter, RegisteredPainters}; use layout::context::{LayoutContext, RegisteredPainter, RegisteredPainters};
use layout::display_list::items::{DisplayList, ScrollOffsetMap, WebRenderImageInfo}; use layout::display_list::items::{DisplayList, ScrollOffsetMap, WebRenderImageInfo};
@ -119,9 +118,6 @@ pub struct LayoutThread {
/// Is the current reflow of an iframe, as opposed to a root window? /// Is the current reflow of an iframe, as opposed to a root window?
is_iframe: bool, is_iframe: bool,
/// The channel on which the font cache can send messages to us.
font_cache_sender: IpcSender<()>,
/// The channel on which messages can be sent to the constellation. /// The channel on which messages can be sent to the constellation.
constellation_chan: IpcSender<ConstellationMsg>, constellation_chan: IpcSender<ConstellationMsg>,
@ -147,9 +143,6 @@ pub struct LayoutThread {
/// This can be used to easily check for invalid stale data. /// This can be used to easily check for invalid stale data.
generation: Cell<u32>, generation: Cell<u32>,
/// The number of Web fonts that have been requested but not yet loaded.
outstanding_web_fonts: Arc<AtomicUsize>,
/// The root of the flow tree. /// The root of the flow tree.
root_flow: RefCell<Option<FlowRef>>, root_flow: RefCell<Option<FlowRef>>,
@ -252,12 +245,18 @@ impl Drop for ScriptReflowResult {
} }
impl Layout for LayoutThread { impl Layout for LayoutThread {
fn handle_constellation_msg(&mut self, msg: script_traits::LayoutControlMsg) { fn handle_constellation_message(
self.handle_request(Request::FromPipeline(msg)); &mut self,
} constellation_message: script_traits::LayoutControlMsg,
) {
fn handle_font_cache_msg(&mut self) { match constellation_message {
self.handle_request(Request::FromFontCache); LayoutControlMsg::SetScrollStates(new_scroll_states) => {
self.set_scroll_states(new_scroll_states);
},
LayoutControlMsg::PaintMetric(epoch, paint_time) => {
self.paint_time_metrics.maybe_set_metric(epoch, paint_time);
},
}
} }
fn device(&self) -> &Device { fn device(&self) -> &Device {
@ -265,7 +264,7 @@ impl Layout for LayoutThread {
} }
fn waiting_for_web_fonts_to_load(&self) -> bool { fn waiting_for_web_fonts_to_load(&self) -> bool {
self.outstanding_web_fonts.load(Ordering::SeqCst) != 0 self.font_context.web_fonts_still_loading() != 0
} }
fn current_epoch(&self) -> Epoch { fn current_epoch(&self) -> Epoch {
@ -548,11 +547,6 @@ impl Layout for LayoutThread {
); );
} }
} }
enum Request {
FromPipeline(LayoutControlMsg),
FromFontCache,
}
impl LayoutThread { impl LayoutThread {
fn root_flow_for_query(&self) -> Option<FlowRef> { fn root_flow_for_query(&self) -> Option<FlowRef> {
self.root_flow.borrow().clone() self.root_flow.borrow().clone()
@ -584,17 +578,6 @@ impl LayoutThread {
Box::new(LayoutFontMetricsProvider), Box::new(LayoutFontMetricsProvider),
); );
// Ask the router to proxy IPC messages from the font cache thread to layout.
let (ipc_font_cache_sender, ipc_font_cache_receiver) = ipc::channel().unwrap();
let cloned_script_chan = script_chan.clone();
ROUTER.add_route(
ipc_font_cache_receiver.to_opaque(),
Box::new(move |_message| {
let _ =
cloned_script_chan.send(ConstellationControlMsg::ForLayoutFromFontCache(id));
}),
);
LayoutThread { LayoutThread {
id, id,
url, url,
@ -606,10 +589,8 @@ impl LayoutThread {
image_cache, image_cache,
font_context, font_context,
first_reflow: Cell::new(true), first_reflow: Cell::new(true),
font_cache_sender: ipc_font_cache_sender,
parallel_flag: true, parallel_flag: true,
generation: Cell::new(0), generation: Cell::new(0),
outstanding_web_fonts: Arc::new(AtomicUsize::new(0)),
root_flow: RefCell::new(None), root_flow: RefCell::new(None),
// Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR // Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR
epoch: Cell::new(Epoch(1)), epoch: Cell::new(Epoch(1)),
@ -685,53 +666,41 @@ impl LayoutThread {
} }
} }
/// Receives and dispatches messages from the script and constellation threads
fn handle_request(&mut self, request: Request) {
match request {
Request::FromFontCache => {
self.outstanding_web_fonts.fetch_sub(1, Ordering::SeqCst);
self.handle_web_font_loaded();
},
Request::FromPipeline(LayoutControlMsg::ExitNow) => self.exit_now(),
Request::FromPipeline(LayoutControlMsg::SetScrollStates(new_scroll_states)) => {
self.set_scroll_states(new_scroll_states);
},
Request::FromPipeline(LayoutControlMsg::PaintMetric(epoch, paint_time)) => {
self.paint_time_metrics.maybe_set_metric(epoch, paint_time);
},
};
}
fn load_all_web_fonts_from_stylesheet_with_guard( fn load_all_web_fonts_from_stylesheet_with_guard(
&self, &self,
stylesheet: &Stylesheet, stylesheet: &Stylesheet,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
) { ) {
// Find all font-face rules and notify the font cache of them. if !stylesheet.is_effective_for_device(self.stylist.device(), guard) {
// GWTODO: Need to handle unloading web fonts. return;
if stylesheet.is_effective_for_device(self.stylist.device(), guard) {
let newly_loading_font_count = self.font_context.add_all_web_fonts_from_stylesheet(
stylesheet,
guard,
self.stylist.device(),
&self.font_cache_sender,
self.debug.load_webfonts_synchronously,
);
if !self.debug.load_webfonts_synchronously {
self.outstanding_web_fonts
.fetch_add(newly_loading_font_count, Ordering::SeqCst);
} else if newly_loading_font_count > 0 {
self.handle_web_font_loaded();
}
} }
}
fn handle_web_font_loaded(&self) { let locked_script_channel = Mutex::new(self.script_chan.clone());
self.font_context.invalidate_caches(); let pipeline_id = self.id;
self.script_chan let web_font_finished_loading_callback = move |succeeded: bool| {
.send(ConstellationControlMsg::WebFontLoaded(self.id)) if succeeded {
.unwrap(); let _ = locked_script_channel
.lock()
.unwrap()
.send(ConstellationControlMsg::WebFontLoaded(pipeline_id));
}
};
// Find all font-face rules and notify the FontContext of them.
// GWTODO: Need to handle unloading web fonts.
let newly_loading_font_count = self.font_context.add_all_web_fonts_from_stylesheet(
stylesheet,
guard,
self.stylist.device(),
Arc::new(web_font_finished_loading_callback) as WebFontLoadFinishedCallback,
self.debug.load_webfonts_synchronously,
);
if self.debug.load_webfonts_synchronously && newly_loading_font_count > 0 {
let _ = self
.script_chan
.send(ConstellationControlMsg::WebFontLoaded(self.id));
}
} }
fn try_get_layout_root<'dom>(&self, node: impl LayoutNode<'dom>) -> Option<FlowRef> { fn try_get_layout_root<'dom>(&self, node: impl LayoutNode<'dom>) -> Option<FlowRef> {

View file

@ -13,8 +13,7 @@ use std::collections::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::process; use std::process;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc;
use std::sync::{Arc, Mutex};
use app_units::Au; use app_units::Au;
use base::id::{BrowsingContextId, PipelineId}; use base::id::{BrowsingContextId, PipelineId};
@ -26,8 +25,8 @@ use fnv::FnvHashMap;
use fxhash::FxHashMap; use fxhash::FxHashMap;
use gfx::font_cache_thread::FontCacheThread; use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::{FontContext, FontContextWebFontMethods}; use gfx::font_context::{FontContext, FontContextWebFontMethods};
use ipc_channel::ipc::{self, IpcSender}; use gfx_traits::WebFontLoadFinishedCallback;
use ipc_channel::router::ROUTER; use ipc_channel::ipc::IpcSender;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list::{DisplayList, WebRenderImageInfo}; use layout::display_list::{DisplayList, WebRenderImageInfo};
use layout::query::{ use layout::query::{
@ -43,7 +42,7 @@ use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory}; use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
use net_traits::image_cache::{ImageCache, UsePlaceholder}; use net_traits::image_cache::{ImageCache, UsePlaceholder};
use net_traits::ResourceThreads; use net_traits::ResourceThreads;
use parking_lot::RwLock; use parking_lot::{Mutex, RwLock};
use profile_traits::mem::{Report, ReportKind}; use profile_traits::mem::{Report, ReportKind};
use profile_traits::path; use profile_traits::path;
use profile_traits::time::{ use profile_traits::time::{
@ -107,9 +106,6 @@ pub struct LayoutThread {
/// Is the current reflow of an iframe, as opposed to a root window? /// Is the current reflow of an iframe, as opposed to a root window?
is_iframe: bool, is_iframe: bool,
/// The channel on which the font cache can send messages to us.
font_cache_sender: IpcSender<()>,
/// The channel on which messages can be sent to the constellation. /// The channel on which messages can be sent to the constellation.
constellation_chan: IpcSender<ConstellationMsg>, constellation_chan: IpcSender<ConstellationMsg>,
@ -132,9 +128,6 @@ pub struct LayoutThread {
/// This can be used to easily check for invalid stale data. /// This can be used to easily check for invalid stale data.
generation: Cell<u32>, generation: Cell<u32>,
/// The number of Web fonts that have been requested but not yet loaded.
outstanding_web_fonts: Arc<AtomicUsize>,
/// The box tree. /// The box tree.
box_tree: RefCell<Option<Arc<BoxTree>>>, box_tree: RefCell<Option<Arc<BoxTree>>>,
@ -228,12 +221,18 @@ impl Drop for ScriptReflowResult {
} }
impl Layout for LayoutThread { impl Layout for LayoutThread {
fn handle_constellation_msg(&mut self, msg: script_traits::LayoutControlMsg) { fn handle_constellation_message(
self.handle_request(Request::FromPipeline(msg)); &mut self,
} constellation_message: script_traits::LayoutControlMsg,
) {
fn handle_font_cache_msg(&mut self) { match constellation_message {
self.handle_request(Request::FromFontCache); LayoutControlMsg::SetScrollStates(new_scroll_states) => {
self.set_scroll_states(new_scroll_states);
},
LayoutControlMsg::PaintMetric(epoch, paint_time) => {
self.paint_time_metrics.maybe_set_metric(epoch, paint_time);
},
}
} }
fn device(&self) -> &Device { fn device(&self) -> &Device {
@ -241,7 +240,7 @@ impl Layout for LayoutThread {
} }
fn waiting_for_web_fonts_to_load(&self) -> bool { fn waiting_for_web_fonts_to_load(&self) -> bool {
self.outstanding_web_fonts.load(Ordering::SeqCst) != 0 self.font_context.web_fonts_still_loading() != 0
} }
fn current_epoch(&self) -> Epoch { fn current_epoch(&self) -> Epoch {
@ -469,11 +468,6 @@ impl Layout for LayoutThread {
} }
} }
enum Request {
FromPipeline(LayoutControlMsg),
FromFontCache,
}
impl LayoutThread { impl LayoutThread {
fn new( fn new(
id: PipelineId, id: PipelineId,
@ -503,17 +497,6 @@ impl LayoutThread {
Box::new(LayoutFontMetricsProvider(font_context.clone())), Box::new(LayoutFontMetricsProvider(font_context.clone())),
); );
// Ask the router to proxy IPC messages from the font cache thread to layout.
let (ipc_font_cache_sender, ipc_font_cache_receiver) = ipc::channel().unwrap();
let cloned_script_chan = script_chan.clone();
ROUTER.add_route(
ipc_font_cache_receiver.to_opaque(),
Box::new(move |_message| {
let _ =
cloned_script_chan.send(ConstellationControlMsg::ForLayoutFromFontCache(id));
}),
);
LayoutThread { LayoutThread {
id, id,
url, url,
@ -525,9 +508,7 @@ impl LayoutThread {
image_cache, image_cache,
font_context, font_context,
first_reflow: Cell::new(true), first_reflow: Cell::new(true),
font_cache_sender: ipc_font_cache_sender,
generation: Cell::new(0), generation: Cell::new(0),
outstanding_web_fonts: Arc::new(AtomicUsize::new(0)),
box_tree: Default::default(), box_tree: Default::default(),
fragment_tree: Default::default(), fragment_tree: Default::default(),
// Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR // Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR
@ -601,55 +582,42 @@ impl LayoutThread {
} }
} }
/// Receives and dispatches messages from the script and constellation threads
fn handle_request(&mut self, request: Request) {
match request {
Request::FromPipeline(LayoutControlMsg::SetScrollStates(new_scroll_states)) => {
self.set_scroll_states(new_scroll_states);
},
Request::FromPipeline(LayoutControlMsg::ExitNow) => {},
Request::FromPipeline(LayoutControlMsg::PaintMetric(epoch, paint_time)) => {
self.paint_time_metrics.maybe_set_metric(epoch, paint_time);
},
Request::FromFontCache => {
self.outstanding_web_fonts.fetch_sub(1, Ordering::SeqCst);
self.handle_web_font_loaded();
},
};
}
fn load_all_web_fonts_from_stylesheet_with_guard( fn load_all_web_fonts_from_stylesheet_with_guard(
&self, &self,
stylesheet: &Stylesheet, stylesheet: &Stylesheet,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
) { ) {
if !stylesheet.is_effective_for_device(self.stylist.device(), guard) {
return;
}
let locked_script_channel = Mutex::new(self.script_chan.clone());
let pipeline_id = self.id;
let web_font_finished_loading_callback = move |succeeded: bool| {
if succeeded {
let _ = locked_script_channel
.lock()
.send(ConstellationControlMsg::WebFontLoaded(pipeline_id));
}
};
// Find all font-face rules and notify the font cache of them. // Find all font-face rules and notify the font cache of them.
// GWTODO: Need to handle unloading web fonts. // GWTODO: Need to handle unloading web fonts.
if stylesheet.is_effective_for_device(self.stylist.device(), guard) { let newly_loading_font_count = self.font_context.add_all_web_fonts_from_stylesheet(
let newly_loading_font_count = self.font_context.add_all_web_fonts_from_stylesheet( stylesheet,
stylesheet, guard,
guard, self.stylist.device(),
self.stylist.device(), Arc::new(web_font_finished_loading_callback) as WebFontLoadFinishedCallback,
&self.font_cache_sender, self.debug.load_webfonts_synchronously,
self.debug.load_webfonts_synchronously, );
);
if !self.debug.load_webfonts_synchronously { if self.debug.load_webfonts_synchronously && newly_loading_font_count > 0 {
self.outstanding_web_fonts let _ = self
.fetch_add(newly_loading_font_count, Ordering::SeqCst); .script_chan
} else if newly_loading_font_count > 0 { .send(ConstellationControlMsg::WebFontLoaded(self.id));
self.handle_web_font_loaded();
}
} }
} }
fn handle_web_font_loaded(&self) {
self.font_context.invalidate_caches();
self.script_chan
.send(ConstellationControlMsg::WebFontLoaded(self.id))
.unwrap();
}
/// The high-level routine that performs layout. /// The high-level routine that performs layout.
fn handle_reflow(&mut self, data: &mut ScriptReflowResult) { fn handle_reflow(&mut self, data: &mut ScriptReflowResult) {
let document = unsafe { ServoLayoutNode::new(&data.document) }; let document = unsafe { ServoLayoutNode::new(&data.document) };
@ -845,7 +813,7 @@ impl LayoutThread {
self.first_reflow.set(false); self.first_reflow.set(false);
data.result.borrow_mut().as_mut().unwrap().pending_images = data.result.borrow_mut().as_mut().unwrap().pending_images =
std::mem::take(&mut *layout_context.pending_images.lock().unwrap()); std::mem::take(&mut *layout_context.pending_images.lock());
if let ReflowGoal::UpdateScrollNode(scroll_state) = data.reflow_goal { if let ReflowGoal::UpdateScrollNode(scroll_state) = data.reflow_goal {
self.update_scroll_node_state(&scroll_state); self.update_scroll_node_state(&scroll_state);
} }

View file

@ -2124,7 +2124,6 @@ impl ScriptThread {
MediaSessionAction(..) => None, MediaSessionAction(..) => None,
SetWebGPUPort(..) => None, SetWebGPUPort(..) => None,
ForLayoutFromConstellation(_, id) => Some(id), ForLayoutFromConstellation(_, id) => Some(id),
ForLayoutFromFontCache(id) => Some(id),
}, },
MixedMessage::FromDevtools(_) => None, MixedMessage::FromDevtools(_) => None,
MixedMessage::FromScript(ref inner_msg) => match *inner_msg { MixedMessage::FromScript(ref inner_msg) => match *inner_msg {
@ -2358,28 +2357,21 @@ impl ScriptThread {
panic!("should have handled {:?} already", msg) panic!("should have handled {:?} already", msg)
}, },
ConstellationControlMsg::ForLayoutFromConstellation(msg, pipeline_id) => { ConstellationControlMsg::ForLayoutFromConstellation(msg, pipeline_id) => {
self.handle_layout_message(msg, pipeline_id) self.handle_layout_message_from_constellation(msg, pipeline_id)
},
ConstellationControlMsg::ForLayoutFromFontCache(pipeline_id) => {
self.handle_font_cache(pipeline_id)
}, },
} }
} }
fn handle_layout_message(&self, msg: LayoutControlMsg, pipeline_id: PipelineId) { fn handle_layout_message_from_constellation(
&self,
msg: LayoutControlMsg,
pipeline_id: PipelineId,
) {
let Some(window) = self.documents.borrow().find_window(pipeline_id) else { let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
warn!("Received layout message pipeline {pipeline_id} closed: {msg:?}."); warn!("Received layout message pipeline {pipeline_id} closed: {msg:?}.");
return; return;
}; };
window.layout_mut().handle_constellation_msg(msg); window.layout_mut().handle_constellation_message(msg);
}
fn handle_font_cache(&self, pipeline_id: PipelineId) {
let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
warn!("Received font cache message pipeline {pipeline_id} closed.");
return;
};
window.layout_mut().handle_font_cache_msg();
} }
fn handle_msg_from_webgpu_server(&self, msg: WebGPUMsg) { fn handle_msg_from_webgpu_server(&self, msg: WebGPUMsg) {

View file

@ -4,6 +4,8 @@
#![deny(unsafe_code)] #![deny(unsafe_code)]
use std::sync::Arc;
use malloc_size_of_derive::MallocSizeOf; use malloc_size_of_derive::MallocSizeOf;
use range::{int_range_index, RangeIndex}; use range::{int_range_index, RangeIndex};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -14,3 +16,5 @@ int_range_index! {
/// the middle of a glyph. /// the middle of a glyph.
struct ByteIndex(isize) struct ByteIndex(isize)
} }
pub type WebFontLoadFinishedCallback = Arc<dyn Fn(bool) + Send + Sync + 'static>;

View file

@ -113,8 +113,6 @@ impl UntrustedNodeAddress {
/// Messages sent to layout from the constellation and/or compositor. /// Messages sent to layout from the constellation and/or compositor.
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub enum LayoutControlMsg { pub enum LayoutControlMsg {
/// Requests that this layout clean up before exit.
ExitNow,
/// Tells layout about the new scrolling offsets of each scrollable stacking context. /// Tells layout about the new scrolling offsets of each scrollable stacking context.
SetScrollStates(Vec<ScrollState>), SetScrollStates(Vec<ScrollState>),
/// Send the paint time for a specific epoch to layout. /// Send the paint time for a specific epoch to layout.
@ -395,8 +393,6 @@ pub enum ConstellationControlMsg {
SetWebGPUPort(IpcReceiver<WebGPUMsg>), SetWebGPUPort(IpcReceiver<WebGPUMsg>),
/// A mesage for a layout from the constellation. /// A mesage for a layout from the constellation.
ForLayoutFromConstellation(LayoutControlMsg, PipelineId), ForLayoutFromConstellation(LayoutControlMsg, PipelineId),
/// A message for a layout from the font cache.
ForLayoutFromFontCache(PipelineId),
} }
impl fmt::Debug for ConstellationControlMsg { impl fmt::Debug for ConstellationControlMsg {
@ -436,7 +432,6 @@ impl fmt::Debug for ConstellationControlMsg {
MediaSessionAction(..) => "MediaSessionAction", MediaSessionAction(..) => "MediaSessionAction",
SetWebGPUPort(..) => "SetWebGPUPort", SetWebGPUPort(..) => "SetWebGPUPort",
ForLayoutFromConstellation(..) => "ForLayoutFromConstellation", ForLayoutFromConstellation(..) => "ForLayoutFromConstellation",
ForLayoutFromFontCache(..) => "ForLayoutFromFontCache",
}; };
write!(formatter, "ConstellationControlMsg::{}", variant) write!(formatter, "ConstellationControlMsg::{}", variant)
} }

View file

@ -179,10 +179,7 @@ pub trait LayoutFactory: Send + Sync {
pub trait Layout { pub trait Layout {
/// Handle a single message from the Constellation. /// Handle a single message from the Constellation.
fn handle_constellation_msg(&mut self, msg: LayoutControlMsg); fn handle_constellation_message(&mut self, msg: LayoutControlMsg);
/// Handle a a single mesasge from the FontCacheThread.
fn handle_font_cache_msg(&mut self);
/// Get a reference to this Layout's Stylo `Device` used to handle media queries and /// Get a reference to this Layout's Stylo `Device` used to handle media queries and
/// resolve font metrics. /// resolve font metrics.