fonts: Simplify FontContext in two ways that affect the unit test (#33541)

This is done by no longer forwarding compositor-bound messages through
SystemFontService and making `FontContext` non-generic:

- Messages from the `FontContext` to the `Compositor` no longer need to be
  forwarded through the `SystemFontService`. Instead send these messages
  directly through the script IPC channel to the `Compositor`.

- Instead of adding a mock `SystemFontServiceProxy`, simply implement a
  mock `SystemFontService` on the other side of an IPC channel in the
  `font_context` unit test. This allows making `FontContext`
  non-generic, greatly simplifying the code. The extra complexity moves
  into the unit test.

These changes necessitate adding a new kind of `FontIdentifier`,
`FontIdentifier::Mock` due to the fact that local fonts have
platform-specific identifiers. This avoids having to pretend like the
system font service can have web fonts -- which was always a bit of a
hack.

These two changes are combined into one PR because they both require
extensive and similar chages in the font_context unit test which
dependended on the details of both of them.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2024-09-25 22:15:47 +02:00 committed by GitHub
parent 1daa0b4fc7
commit ac567645a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 482 additions and 425 deletions

View file

@ -11,7 +11,7 @@ use euclid::default::{Box2D, Point2D, Rect, Size2D, Transform2D, Vector2D};
use euclid::point2;
use fonts::{
ByteIndex, FontBaseline, FontContext, FontGroup, FontMetrics, FontRef, GlyphInfo, GlyphStore,
ShapingFlags, ShapingOptions, SystemFontServiceProxy, LAST_RESORT_GLYPH_ADVANCE,
ShapingFlags, ShapingOptions, LAST_RESORT_GLYPH_ADVANCE,
};
use ipc_channel::ipc::{IpcSender, IpcSharedMemory};
use log::{debug, warn};
@ -434,7 +434,7 @@ pub struct CanvasData<'a> {
old_image_key: Option<ImageKey>,
/// An old webrender image key that can be deleted when the current epoch ends.
very_old_image_key: Option<ImageKey>,
font_context: Arc<FontContext<SystemFontServiceProxy>>,
font_context: Arc<FontContext>,
}
fn create_backend() -> Box<dyn Backend> {
@ -446,7 +446,7 @@ impl<'a> CanvasData<'a> {
size: Size2D<u64>,
webrender_api: Box<dyn WebrenderApi>,
antialias: AntialiasMode,
font_context: Arc<FontContext<SystemFontServiceProxy>>,
font_context: Arc<FontContext>,
) -> CanvasData<'a> {
let backend = create_backend();
let draw_target = backend.create_drawtarget(size);

View file

@ -17,7 +17,7 @@ use ipc_channel::router::ROUTER;
use log::warn;
use net_traits::ResourceThreads;
use webrender_api::ImageKey;
use webrender_traits::ImageUpdate;
use webrender_traits::{ImageUpdate, WebRenderScriptApi};
use crate::canvas_data::*;
@ -37,7 +37,7 @@ pub struct CanvasPaintThread<'a> {
canvases: HashMap<CanvasId, CanvasData<'a>>,
next_canvas_id: CanvasId,
webrender_api: Box<dyn WebrenderApi>,
font_context: Arc<FontContext<SystemFontServiceProxy>>,
font_context: Arc<FontContext>,
}
impl<'a> CanvasPaintThread<'a> {
@ -46,11 +46,18 @@ impl<'a> CanvasPaintThread<'a> {
system_font_service: Arc<SystemFontServiceProxy>,
resource_threads: ResourceThreads,
) -> CanvasPaintThread<'a> {
// This is only used for web fonts and currently canvas never uses web fonts.
let webrender_script_api = WebRenderScriptApi::dummy();
CanvasPaintThread {
canvases: HashMap::new(),
next_canvas_id: CanvasId(0),
webrender_api,
font_context: Arc::new(FontContext::new(system_font_service, resource_threads)),
font_context: Arc::new(FontContext::new(
system_font_service,
webrender_script_api,
resource_threads,
)),
}
}

View file

@ -9,6 +9,7 @@ use std::fs::{create_dir_all, File};
use std::io::Write;
use std::iter::once;
use std::rc::Rc;
use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use base::cross_process_instant::CrossProcessInstant;
@ -23,7 +24,7 @@ use embedder_traits::Cursor;
use euclid::{Point2D, Rect, Scale, Transform3D, Vector2D};
use fnv::{FnvHashMap, FnvHashSet};
use image::{DynamicImage, ImageFormat};
use ipc_channel::ipc;
use ipc_channel::ipc::{self, IpcSharedMemory};
use libc::c_void;
use log::{debug, error, info, trace, warn};
use pixels::{CorsStatus, Image, PixelFormat};
@ -43,9 +44,10 @@ use webrender_api::units::{
};
use webrender_api::{
self, BuiltDisplayList, DirtyRect, DisplayListPayload, DocumentId, Epoch as WebRenderEpoch,
ExternalScrollId, FontInstanceOptions, HitTestFlags, PipelineId as WebRenderPipelineId,
PropertyBinding, ReferenceFrameKind, RenderReasons, SampledScrollOffset, ScrollLocation,
SpaceAndClipInfo, SpatialId, SpatialTreeItemKey, TransformStyle,
ExternalScrollId, FontInstanceFlags, FontInstanceKey, FontInstanceOptions, FontKey,
HitTestFlags, PipelineId as WebRenderPipelineId, PropertyBinding, ReferenceFrameKind,
RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo, SpatialId,
SpatialTreeItemKey, TransformStyle,
};
use webrender_traits::display_list::{HitTestInfo, ScrollTree};
use webrender_traits::{
@ -841,6 +843,23 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
.send_transaction(self.webrender_document, txn);
},
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::AddFont(
data,
index,
key_sender,
)) => {
let _ = key_sender.send(self.add_font(index, data));
},
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::AddFontInstance(
font_key,
size,
flags,
sender,
)) => {
let _ = sender.send(self.add_font_instance(font_key, size, flags));
},
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::RemoveFonts(
keys,
instance_keys,
@ -865,47 +884,24 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
.send_transaction(self.webrender_document, txn);
},
ForwardedToCompositorMsg::Font(FontToCompositorMsg::AddFontInstance(
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFontInstance(
font_key,
size,
flags,
sender,
)) => {
let key = self.webrender_api.generate_font_instance_key();
let mut transaction = Transaction::new();
let font_instance_options = FontInstanceOptions {
flags,
..Default::default()
};
transaction.add_font_instance(
key,
font_key,
size,
Some(font_instance_options),
None,
Vec::new(),
);
self.webrender_api
.send_transaction(self.webrender_document, transaction);
let _ = sender.send(key);
let _ = sender.send(self.add_font_instance(font_key, size, flags));
},
ForwardedToCompositorMsg::Font(FontToCompositorMsg::AddFont(
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFont(
key_sender,
index,
data,
)) => {
let font_key = self.webrender_api.generate_font_key();
let mut transaction = Transaction::new();
transaction.add_raw_font(font_key, (**data).into(), index);
self.webrender_api
.send_transaction(self.webrender_document, transaction);
let _ = key_sender.send(font_key);
let _ = key_sender.send(self.add_font(index, data));
},
ForwardedToCompositorMsg::Font(FontToCompositorMsg::AddSystemFont(
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddSystemFont(
key_sender,
native_handle,
)) => {
@ -960,16 +956,26 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
self.remove_pipeline_root_layer(pipeline_id);
let _ = sender.send(());
},
CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font(
CompositorMsg::Forwarded(ForwardedToCompositorMsg::SystemFontService(
FontToCompositorMsg::AddFontInstance(_, _, _, sender),
)) => {
let _ = sender.send(self.webrender_api.generate_font_instance_key());
},
CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font(
CompositorMsg::Forwarded(ForwardedToCompositorMsg::Layout(
ScriptToCompositorMsg::AddFontInstance(_, _, _, sender),
)) => {
let _ = sender.send(self.webrender_api.generate_font_instance_key());
},
CompositorMsg::Forwarded(ForwardedToCompositorMsg::SystemFontService(
FontToCompositorMsg::AddFont(sender, _, _),
)) => {
let _ = sender.send(self.webrender_api.generate_font_key());
},
CompositorMsg::Forwarded(ForwardedToCompositorMsg::Layout(
ScriptToCompositorMsg::AddFont(_, _, sender),
)) => {
let _ = sender.send(self.webrender_api.generate_font_key());
},
CompositorMsg::Forwarded(ForwardedToCompositorMsg::Canvas(
CanvasToCompositorMsg::GenerateKey(sender),
)) => {
@ -2518,4 +2524,40 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
eprintln!("Unable to write servo version for WebRender Capture: {error:?}");
}
}
fn add_font_instance(
&mut self,
font_key: FontKey,
size: f32,
flags: FontInstanceFlags,
) -> FontInstanceKey {
let instance_key = self.webrender_api.generate_font_instance_key();
let mut transaction = Transaction::new();
let font_instance_options = FontInstanceOptions {
flags,
..Default::default()
};
transaction.add_font_instance(
instance_key,
font_key,
size,
Some(font_instance_options),
None,
Vec::new(),
);
self.webrender_api
.send_transaction(self.webrender_document, transaction);
instance_key
}
fn add_font(&mut self, index: u32, data: Arc<IpcSharedMemory>) -> FontKey {
let font_key = self.webrender_api.generate_font_key();
let mut transaction = Transaction::new();
transaction.add_raw_font(font_key, (**data).into(), index);
self.webrender_api
.send_transaction(self.webrender_document, transaction);
font_key
}
}

View file

@ -31,7 +31,7 @@ use crate::font_context::FontContext;
use crate::font_template::{FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods};
use crate::platform::font::{FontTable, PlatformFont};
pub use crate::platform::font_list::fallback_font_families;
use crate::system_font_service::{FontIdentifier, SystemFontServiceProxyTrait};
use crate::system_font_service::FontIdentifier;
use crate::{
ByteIndex, EmojiPresentationPreference, FallbackFontSelectionOptions, FontData, GlyphData,
GlyphId, GlyphStore, Shaper,
@ -550,9 +550,9 @@ impl FontGroup {
/// `codepoint`. If no such font is found, returns the first available font or fallback font
/// (which will cause a "glyph not found" character to be rendered). If no font at all can be
/// found, returns None.
pub fn find_by_codepoint<S: SystemFontServiceProxyTrait>(
pub fn find_by_codepoint(
&mut self,
font_context: &FontContext<S>,
font_context: &FontContext,
codepoint: char,
next_codepoint: Option<char>,
) -> Option<FontRef> {
@ -622,10 +622,7 @@ impl FontGroup {
}
/// Find the first available font in the group, or the first available fallback font.
pub fn first<S: SystemFontServiceProxyTrait>(
&mut self,
font_context: &FontContext<S>,
) -> Option<FontRef> {
pub fn first(&mut self, font_context: &FontContext) -> Option<FontRef> {
// From https://drafts.csswg.org/css-fonts/#first-available-font:
// > The first available font, used for example in the definition of font-relative lengths
// > such as ex or in the definition of the line-height property, is defined to be the first
@ -649,14 +646,13 @@ impl FontGroup {
/// Attempts to find a font which matches the given `template_predicate` and `font_predicate`.
/// This method mutates because we may need to load new font data in the process of finding
/// a suitable font.
fn find<S, TemplatePredicate, FontPredicate>(
fn find<TemplatePredicate, FontPredicate>(
&mut self,
font_context: &FontContext<S>,
font_context: &FontContext,
template_predicate: TemplatePredicate,
font_predicate: FontPredicate,
) -> Option<FontRef>
where
S: SystemFontServiceProxyTrait,
TemplatePredicate: Fn(FontTemplateRef) -> bool,
FontPredicate: Fn(&FontRef) -> bool,
{
@ -678,15 +674,14 @@ impl FontGroup {
/// `font_predicate`. The default family (i.e. "serif") will be tried first, followed by
/// platform-specific family names. If a `codepoint` is provided, then its Unicode block may be
/// used to refine the list of family names which will be tried.
fn find_fallback<S, TemplatePredicate, FontPredicate>(
fn find_fallback<TemplatePredicate, FontPredicate>(
&mut self,
font_context: &FontContext<S>,
font_context: &FontContext,
options: FallbackFontSelectionOptions,
template_predicate: TemplatePredicate,
font_predicate: FontPredicate,
) -> Option<FontRef>
where
S: SystemFontServiceProxyTrait,
TemplatePredicate: Fn(FontTemplateRef) -> bool,
FontPredicate: Fn(&FontRef) -> bool,
{
@ -750,15 +745,14 @@ impl FontGroupFamily {
}
}
fn find<S, TemplatePredicate, FontPredicate>(
fn find<TemplatePredicate, FontPredicate>(
&mut self,
font_descriptor: &FontDescriptor,
font_context: &FontContext<S>,
font_context: &FontContext,
template_predicate: &TemplatePredicate,
font_predicate: &FontPredicate,
) -> Option<FontRef>
where
S: SystemFontServiceProxyTrait,
TemplatePredicate: Fn(FontTemplateRef) -> bool,
FontPredicate: Fn(&FontRef) -> bool,
{
@ -781,10 +775,10 @@ impl FontGroupFamily {
.next()
}
fn members<S: SystemFontServiceProxyTrait>(
fn members(
&mut self,
font_descriptor: &FontDescriptor,
font_context: &FontContext<S>,
font_context: &FontContext,
) -> impl Iterator<Item = &mut FontGroupFamilyMember> {
let family_descriptor = &self.family_descriptor;
let members = self.members.get_or_insert_with(|| {

View file

@ -12,6 +12,7 @@ use app_units::Au;
use crossbeam_channel::unbounded;
use fnv::FnvHasher;
use fonts_traits::WebFontLoadFinishedCallback;
use ipc_channel::ipc;
use log::{debug, trace};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use malloc_size_of_derive::MallocSizeOf;
@ -29,7 +30,8 @@ use style::stylesheets::{CssRule, DocumentStyleSheet, FontFaceRule, StylesheetIn
use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFontFamily};
use style::Atom;
use url::Url;
use webrender_api::{FontInstanceKey, FontKey};
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
use webrender_traits::{ScriptToCompositorMsg, WebRenderScriptApi};
use crate::font::{
Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope,
@ -37,10 +39,8 @@ use crate::font::{
use crate::font_store::{CrossThreadFontStore, CrossThreadWebRenderFontStore};
use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods};
use crate::platform::font::PlatformFont;
use crate::system_font_service::{
CSSFontFaceDescriptors, FontIdentifier, SystemFontServiceProxyTrait,
};
use crate::{FontData, LowercaseFontFamilyName, PlatformFontMethods};
use crate::system_font_service::{CSSFontFaceDescriptors, FontIdentifier};
use crate::{FontData, LowercaseFontFamilyName, PlatformFontMethods, SystemFontServiceProxy};
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
@ -48,10 +48,13 @@ static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
/// working with fonts. It is the public API used by the layout and
/// paint code. It talks directly to the system font service where
/// required.
pub struct FontContext<Proxy: SystemFontServiceProxyTrait> {
pub(crate) system_font_service_proxy: Arc<Proxy>,
pub struct FontContext {
pub(crate) system_font_service_proxy: Arc<SystemFontServiceProxy>,
resource_threads: ReentrantMutex<CoreResourceThread>,
/// A sender that can send messages and receive replies from the compositor.
webrender_api: ReentrantMutex<WebRenderScriptApi>,
/// The actual instances of fonts ie a [`FontTemplate`] combined with a size and
/// other font properties, along with the font data and a platform font instance.
fonts: RwLock<HashMap<FontCacheKey, Option<FontRef>>>,
@ -67,7 +70,7 @@ pub struct FontContext<Proxy: SystemFontServiceProxyTrait> {
have_removed_web_fonts: AtomicBool,
}
impl<S: SystemFontServiceProxyTrait> MallocSizeOf for FontContext<S> {
impl MallocSizeOf for FontContext {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
let font_cache_size = self
.fonts
@ -87,12 +90,17 @@ impl<S: SystemFontServiceProxyTrait> MallocSizeOf for FontContext<S> {
}
}
impl<Proxy: SystemFontServiceProxyTrait> FontContext<Proxy> {
pub fn new(system_font_service_proxy: Arc<Proxy>, resource_threads: ResourceThreads) -> Self {
impl FontContext {
pub fn new(
system_font_service_proxy: Arc<SystemFontServiceProxy>,
webrender_api: WebRenderScriptApi,
resource_threads: ResourceThreads,
) -> Self {
#[allow(clippy::default_constructed_unit_structs)]
Self {
system_font_service_proxy,
resource_threads: ReentrantMutex::new(resource_threads.core_thread),
webrender_api: ReentrantMutex::new(webrender_api),
fonts: Default::default(),
resolved_font_groups: Default::default(),
web_fonts: Arc::new(RwLock::default()),
@ -106,11 +114,13 @@ impl<Proxy: SystemFontServiceProxyTrait> FontContext<Proxy> {
}
pub(crate) fn get_font_data(&self, identifier: &FontIdentifier) -> Arc<FontData> {
self.web_fonts
.read()
.get_font_data(identifier)
.or_else(|| self.system_font_service_proxy.get_font_data(identifier))
.expect("Could not find font data")
match identifier {
FontIdentifier::Web(_) => self.web_fonts.read().get_font_data(identifier),
FontIdentifier::Local(_) | FontIdentifier::Mock(_) => {
self.system_font_service_proxy.get_font_data(identifier)
},
}
.expect("Could not find font data")
}
/// Handle the situation where a web font finishes loading, specifying if the load suceeded or failed.
@ -272,11 +282,13 @@ impl<Proxy: SystemFontServiceProxyTrait> FontContext<Proxy> {
)?;
font.font_key = match font_template.identifier() {
FontIdentifier::Local(_) => self.system_font_service_proxy.get_system_font_instance(
font_template.identifier(),
font_descriptor.pt_size,
font.webrender_font_instance_flags(),
),
FontIdentifier::Local(_) | FontIdentifier::Mock(_) => {
self.system_font_service_proxy.get_system_font_instance(
font_template.identifier(),
font_descriptor.pt_size,
font.webrender_font_instance_flags(),
)
},
FontIdentifier::Web(_) => self.webrender_font_store.write().get_font_instance(
self,
font_template.clone(),
@ -291,6 +303,42 @@ impl<Proxy: SystemFontServiceProxyTrait> FontContext<Proxy> {
fn invalidate_font_groups_after_web_font_load(&self) {
self.resolved_font_groups.write().clear();
}
pub(crate) fn get_web_font(&self, data: Arc<FontData>, index: u32) -> FontKey {
let (result_sender, result_receiver) =
ipc::channel().expect("failed to create IPC channel");
let _ = self
.webrender_api
.lock()
.sender()
.send(ScriptToCompositorMsg::AddFont(
data.as_ipc_shared_memory(),
index,
result_sender,
));
result_receiver.recv().unwrap()
}
pub(crate) fn get_web_font_instance(
&self,
font_key: FontKey,
font_size: f32,
font_flags: FontInstanceFlags,
) -> FontInstanceKey {
let (result_sender, result_receiver) =
ipc::channel().expect("failed to create IPC channel");
let _ = self
.webrender_api
.lock()
.sender()
.send(ScriptToCompositorMsg::AddFontInstance(
font_key,
font_size,
font_flags,
result_sender,
));
result_receiver.recv().unwrap()
}
}
#[derive(Clone)]
@ -318,7 +366,7 @@ pub trait FontContextWebFontMethods {
-> (Vec<FontKey>, Vec<FontInstanceKey>);
}
impl<S: SystemFontServiceProxyTrait + 'static> FontContextWebFontMethods for Arc<FontContext<S>> {
impl FontContextWebFontMethods for Arc<FontContext> {
fn add_all_web_fonts_from_stylesheet(
&self,
stylesheet: &DocumentStyleSheet,
@ -523,8 +571,8 @@ impl<S: SystemFontServiceProxyTrait + 'static> FontContextWebFontMethods for Arc
}
}
struct RemoteWebFontDownloader<Proxy: SystemFontServiceProxyTrait> {
font_context: Arc<FontContext<Proxy>>,
struct RemoteWebFontDownloader {
font_context: Arc<FontContext>,
url: ServoArc<Url>,
web_font_family_name: LowercaseFontFamilyName,
response_valid: Mutex<bool>,
@ -537,10 +585,10 @@ enum DownloaderResponseResult {
Failure,
}
impl<Proxy: SystemFontServiceProxyTrait + 'static> RemoteWebFontDownloader<Proxy> {
impl RemoteWebFontDownloader {
fn download(
url_source: UrlSource,
font_context: Arc<FontContext<Proxy>>,
font_context: Arc<FontContext>,
web_font_family_name: LowercaseFontFamilyName,
state: WebFontDownloadState,
) {
@ -628,12 +676,11 @@ impl<Proxy: SystemFontServiceProxyTrait + 'static> RemoteWebFontDownloader<Proxy
descriptor
.override_values_with_css_font_template_descriptors(&state.css_font_face_descriptors);
let Ok(new_template) =
FontTemplate::new_for_remote_web_font(url, descriptor, Some(state.stylesheet.clone()))
else {
return false;
};
let new_template = FontTemplate::new(
FontIdentifier::Web(url),
descriptor,
Some(state.stylesheet.clone()),
);
let not_cancelled = self.font_context.web_fonts.write().handle_web_font_loaded(
state,
new_template,

View file

@ -18,9 +18,7 @@ use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
use crate::font::FontDescriptor;
use crate::font_context::WebFontDownloadState;
use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods, IsOblique};
use crate::system_font_service::{
FontIdentifier, LowercaseFontFamilyName, SystemFontServiceProxyTrait,
};
use crate::system_font_service::{FontIdentifier, LowercaseFontFamilyName};
use crate::FontContext;
/// A data structure to store data for fonts. If sent across IPC channels and only a
@ -171,7 +169,7 @@ impl FontStore {
FontIdentifier::Local(local_identifier) => {
Arc::new(FontData::from_bytes(local_identifier.read_data_from_file()))
},
FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."),
_ => unreachable!("Web and mock fonts should always have data."),
})
}
@ -196,9 +194,9 @@ pub struct WebRenderFontStore {
pub(crate) type CrossThreadWebRenderFontStore = Arc<RwLock<WebRenderFontStore>>;
impl WebRenderFontStore {
pub(crate) fn get_font_instance<Proxy: SystemFontServiceProxyTrait>(
pub(crate) fn get_font_instance(
&mut self,
font_context: &FontContext<Proxy>,
font_context: &FontContext,
font_template: FontTemplateRef,
pt_size: Au,
flags: FontInstanceFlags,
@ -210,18 +208,14 @@ impl WebRenderFontStore {
.entry(identifier.clone())
.or_insert_with(|| {
let data = font_context.get_font_data(&identifier);
font_context
.system_font_service_proxy
.get_web_font(data, identifier.index())
font_context.get_web_font(data, identifier.index())
});
*self
.webrender_font_instance_map
.entry((font_key, pt_size))
.or_insert_with(|| {
font_context
.system_font_service_proxy
.get_web_font_instance(font_key, pt_size.to_f32_px(), flags)
font_context.get_web_font_instance(font_key, pt_size.to_f32_px(), flags)
})
}

View file

@ -9,14 +9,12 @@ use std::sync::Arc;
use atomic_refcell::AtomicRefCell;
use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize};
use servo_url::ServoUrl;
use style::computed_values::font_stretch::T as FontStretch;
use style::computed_values::font_style::T as FontStyle;
use style::stylesheets::DocumentStyleSheet;
use style::values::computed::font::FontWeight;
use crate::font::FontDescriptor;
use crate::platform::font_list::LocalFontIdentifier;
use crate::system_font_service::{
CSSFontFaceDescriptors, ComputedFontStyleDescriptor, FontIdentifier,
};
@ -160,29 +158,17 @@ impl Debug for FontTemplate {
/// is common, regardless of the number of instances of
/// this font handle per thread.
impl FontTemplate {
/// Create a new [`FontTemplate`] for a system font installed locally.
pub fn new_for_local_font(
identifier: LocalFontIdentifier,
descriptor: FontTemplateDescriptor,
) -> FontTemplate {
FontTemplate {
identifier: FontIdentifier::Local(identifier),
descriptor,
stylesheet: None,
}
}
/// Create a new [`FontTemplate`] for a `@font-family` with a `url(...)` `src` font.
pub fn new_for_remote_web_font(
url: ServoUrl,
/// Create a new [`FontTemplate`].
pub fn new(
identifier: FontIdentifier,
descriptor: FontTemplateDescriptor,
stylesheet: Option<DocumentStyleSheet>,
) -> Result<FontTemplate, &'static str> {
Ok(FontTemplate {
identifier: FontIdentifier::Web(url),
) -> FontTemplate {
FontTemplate {
identifier,
descriptor,
stylesheet,
})
}
}
/// Create a new [`FontTemplate`] for a `@font-family` with a `local(...)` `src`. This takes in

View file

@ -19,7 +19,8 @@ use style::Atom;
use super::xml::{Attribute, Node};
use crate::{
FallbackFontSelectionOptions, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName,
FallbackFontSelectionOptions, FontIdentifier, FontTemplate, FontTemplateDescriptor,
LowercaseFontFamilyName,
};
static FONT_LIST: LazyLock<FontList> = LazyLock::new(|| FontList::new());
@ -491,9 +492,10 @@ where
None => StyleFontStyle::NORMAL,
};
let descriptor = FontTemplateDescriptor::new(weight, stretch, style);
callback(FontTemplate::new_for_local_font(
local_font_identifier,
callback(FontTemplate::new(
FontIdentifier::Local(local_font_identifier),
descriptor,
None,
));
};

View file

@ -36,7 +36,10 @@ use super::c_str_to_string;
use crate::font::map_platform_values_to_style_values;
use crate::font_template::{FontTemplate, FontTemplateDescriptor};
use crate::platform::add_noto_fallback_families;
use crate::{EmojiPresentationPreference, FallbackFontSelectionOptions, LowercaseFontFamilyName};
use crate::{
EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier,
LowercaseFontFamilyName,
};
/// An identifier for a local font on systems using Freetype.
#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
@ -155,9 +158,10 @@ where
};
let descriptor = FontTemplateDescriptor::new(weight, stretch, style);
callback(FontTemplate::new_for_local_font(
local_font_identifier,
callback(FontTemplate::new(
FontIdentifier::Local(local_font_identifier),
descriptor,
None,
))
}

View file

@ -22,7 +22,7 @@ use style::Atom;
use unicode_script::Script;
use crate::{
EmojiPresentationPreference, FallbackFontSelectionOptions, FontTemplate,
EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate,
FontTemplateDescriptor, LowercaseFontFamilyName,
};
@ -492,9 +492,10 @@ where
None => StyleFontStyle::NORMAL,
};
let descriptor = FontTemplateDescriptor::new(weight, stretch, style);
callback(FontTemplate::new_for_local_font(
local_font_identifier,
callback(FontTemplate::new(
FontIdentifier::Local(local_font_identifier),
descriptor,
None,
));
};

View file

@ -86,7 +86,7 @@ impl CoreTextFontCache {
core_text::font::new_from_descriptor(&descriptor, clamped_pt_size)
},
FontIdentifier::Web(_) => {
FontIdentifier::Web(_) | FontIdentifier::Mock(_) => {
let provider = CGDataProvider::from_buffer(data);
let cgfont = CGFont::from_data_provider(provider).ok()?;
core_text::font::new_from_CGFont(&cgfont, clamped_pt_size)

View file

@ -18,7 +18,7 @@ use webrender_api::NativeFontHandle;
use crate::platform::add_noto_fallback_families;
use crate::platform::font::CoreTextFontTraitsMapping;
use crate::{
EmojiPresentationPreference, FallbackFontSelectionOptions, FontTemplate,
EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate,
FontTemplateDescriptor, LowercaseFontFamilyName,
};
@ -85,7 +85,11 @@ where
postscript_name: Atom::from(family_descriptor.font_name()),
path: Atom::from(path),
};
callback(FontTemplate::new_for_local_font(identifier, descriptor));
callback(FontTemplate::new(
FontIdentifier::Local(identifier),
descriptor,
None,
));
}
}
}

View file

@ -14,7 +14,7 @@ use style::values::computed::{FontStyle as StyleFontStyle, FontWeight as StyleFo
use style::values::specified::font::FontStretchKeyword;
use crate::{
EmojiPresentationPreference, FallbackFontSelectionOptions, FontTemplate,
EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate,
FontTemplateDescriptor, LowercaseFontFamilyName,
};
@ -78,9 +78,10 @@ where
let local_font_identifier = LocalFontIdentifier {
font_descriptor: Arc::new(font.to_descriptor()),
};
callback(FontTemplate::new_for_local_font(
local_font_identifier,
callback(FontTemplate::new(
FontIdentifier::Local(local_font_identifier),
template_descriptor,
None,
))
}
}

View file

@ -24,6 +24,7 @@ use style::values::computed::font::{
};
use style::values::computed::{FontStretch, FontWeight};
use style::values::specified::FontStretch as SpecifiedFontStretch;
use style::Atom;
use tracing::{span, Level};
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
use webrender_traits::WebRenderFontApi;
@ -41,26 +42,27 @@ use crate::FontData;
pub enum FontIdentifier {
Local(LocalFontIdentifier),
Web(ServoUrl),
Mock(Atom),
}
impl FontIdentifier {
pub fn index(&self) -> u32 {
match *self {
Self::Local(ref local_font_identifier) => local_font_identifier.index(),
Self::Web(_) => 0,
Self::Web(_) | Self::Mock(_) => 0,
}
}
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct FontTemplateRequestResult {
templates: Vec<FontTemplate>,
template_data: Vec<(FontIdentifier, Arc<FontData>)>,
pub templates: Vec<FontTemplate>,
pub template_data: Vec<(FontIdentifier, Arc<FontData>)>,
}
/// Commands that the `FontContext` sends to the `SystemFontService`.
#[derive(Debug, Deserialize, Serialize)]
pub enum Command {
pub enum SystemFontServiceMessage {
GetFontTemplates(
Option<FontDescriptor>,
SingleFontFamily,
@ -72,8 +74,6 @@ pub enum Command {
FontInstanceFlags,
IpcSender<FontInstanceKey>,
),
GetWebFont(Arc<FontData>, u32, IpcSender<FontKey>),
GetWebFontInstance(FontKey, f32, FontInstanceFlags, IpcSender<FontInstanceKey>),
Exit(IpcSender<()>),
Ping,
}
@ -93,7 +93,7 @@ struct ResolvedGenericFontFamilies {
/// responsible for reading the list of system fonts, handling requests to match against
/// them, and ensuring that only one copy of system font data is loaded at a time.
pub struct SystemFontService {
port: IpcReceiver<Command>,
port: IpcReceiver<SystemFontServiceMessage>,
local_families: FontStore,
webrender_api: Box<dyn WebRenderFontApi>,
webrender_fonts: HashMap<FontIdentifier, FontKey>,
@ -102,7 +102,7 @@ pub struct SystemFontService {
}
#[derive(Clone, Deserialize, Serialize)]
pub struct SystemFontServiceProxySender(IpcSender<Command>);
pub struct SystemFontServiceProxySender(pub IpcSender<SystemFontServiceMessage>);
impl SystemFontServiceProxySender {
pub fn to_proxy(&self) -> SystemFontServiceProxy {
@ -145,37 +145,21 @@ impl SystemFontService {
let msg = self.port.recv().unwrap();
match msg {
Command::GetFontTemplates(font_descriptor, font_family, result_sender) => {
SystemFontServiceMessage::GetFontTemplates(
font_descriptor,
font_family,
result_sender,
) => {
let span = span!(Level::TRACE, "GetFontTemplates", servo_profiling = true);
let _span = span.enter();
let _ =
result_sender.send(self.get_font_templates(font_descriptor, font_family));
},
Command::GetFontInstance(identifier, pt_size, flags, result) => {
SystemFontServiceMessage::GetFontInstance(identifier, pt_size, flags, result) => {
let _ = result.send(self.get_font_instance(identifier, pt_size, flags));
},
Command::GetWebFont(data, font_index, result_sender) => {
self.webrender_api.forward_add_font_message(
data.as_ipc_shared_memory(),
font_index,
result_sender,
);
},
Command::GetWebFontInstance(
font_key,
font_size,
font_instance_flags,
result_sender,
) => {
self.webrender_api.forward_add_font_instance_message(
font_key,
font_size,
font_instance_flags,
result_sender,
);
},
Command::Ping => (),
Command::Exit(result) => {
SystemFontServiceMessage::Ping => (),
SystemFontServiceMessage::Exit(result) => {
let _ = result.send(());
break;
},
@ -325,40 +309,17 @@ impl SystemFontService {
}
}
/// A trait for accessing the [`SystemFontServiceProxy`] necessary for unit testing.
pub trait SystemFontServiceProxyTrait: Send + Sync {
fn find_matching_font_templates(
&self,
descriptor_to_match: Option<&FontDescriptor>,
font_family_name: &SingleFontFamily,
) -> Vec<FontTemplateRef>;
fn get_system_font_instance(
&self,
font_identifier: FontIdentifier,
size: Au,
flags: FontInstanceFlags,
) -> FontInstanceKey;
fn get_web_font(&self, data: Arc<FontData>, index: u32) -> FontKey;
fn get_web_font_instance(
&self,
font_key: FontKey,
size: f32,
flags: FontInstanceFlags,
) -> FontInstanceKey;
fn get_font_data(&self, identifier: &FontIdentifier) -> Option<Arc<FontData>>;
}
#[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 (via [`SystemFontServiceProxyTrait`]).
/// The public interface to the [`SystemFontService`], used by per-Document
/// `FontContext` instances.
#[derive(Debug)]
pub struct SystemFontServiceProxy {
sender: ReentrantMutex<IpcSender<Command>>,
sender: ReentrantMutex<IpcSender<SystemFontServiceMessage>>,
templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
data_cache: RwLock<HashMap<FontIdentifier, Arc<FontData>>>,
}
@ -453,7 +414,7 @@ impl SystemFontServiceProxy {
let (response_chan, response_port) = ipc::channel().unwrap();
self.sender
.lock()
.send(Command::Exit(response_chan))
.send(SystemFontServiceMessage::Exit(response_chan))
.expect("Couldn't send SystemFontService exit message");
response_port
.recv()
@ -463,10 +424,8 @@ impl SystemFontServiceProxy {
pub fn to_sender(&self) -> SystemFontServiceProxySender {
SystemFontServiceProxySender(self.sender.lock().clone())
}
}
impl SystemFontServiceProxyTrait for SystemFontServiceProxy {
fn get_system_font_instance(
pub(crate) fn get_system_font_instance(
&self,
identifier: FontIdentifier,
size: Au,
@ -475,7 +434,7 @@ impl SystemFontServiceProxyTrait for SystemFontServiceProxy {
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
self.sender
.lock()
.send(Command::GetFontInstance(
.send(SystemFontServiceMessage::GetFontInstance(
identifier,
size,
flags,
@ -485,7 +444,11 @@ impl SystemFontServiceProxyTrait for SystemFontServiceProxy {
let instance_key = response_port.recv();
if instance_key.is_err() {
let font_thread_has_closed = self.sender.lock().send(Command::Ping).is_err();
let font_thread_has_closed = self
.sender
.lock()
.send(SystemFontServiceMessage::Ping)
.is_err();
assert!(
font_thread_has_closed,
"Failed to receive a response from live font cache"
@ -495,7 +458,7 @@ impl SystemFontServiceProxyTrait for SystemFontServiceProxy {
instance_key.unwrap()
}
fn find_matching_font_templates(
pub(crate) fn find_matching_font_templates(
&self,
descriptor_to_match: Option<&FontDescriptor>,
family_descriptor: &SingleFontFamily,
@ -516,7 +479,7 @@ impl SystemFontServiceProxyTrait for SystemFontServiceProxy {
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
self.sender
.lock()
.send(Command::GetFontTemplates(
.send(SystemFontServiceMessage::GetFontTemplates(
descriptor_to_match.cloned(),
family_descriptor.clone(),
response_chan,
@ -525,7 +488,11 @@ impl SystemFontServiceProxyTrait for SystemFontServiceProxy {
let reply = response_port.recv();
let Ok(reply) = reply else {
let font_thread_has_closed = self.sender.lock().send(Command::Ping).is_err();
let font_thread_has_closed = self
.sender
.lock()
.send(SystemFontServiceMessage::Ping)
.is_err();
assert!(
font_thread_has_closed,
"Failed to receive a response from live font cache"
@ -544,34 +511,7 @@ impl SystemFontServiceProxyTrait for SystemFontServiceProxy {
templates
}
fn get_web_font(&self, data: Arc<FontData>, index: u32) -> FontKey {
let (result_sender, result_receiver) =
ipc::channel().expect("failed to create IPC channel");
let _ = self
.sender
.lock()
.send(Command::GetWebFont(data, index, result_sender));
result_receiver.recv().unwrap()
}
fn get_web_font_instance(
&self,
font_key: FontKey,
font_size: f32,
font_flags: FontInstanceFlags,
) -> FontInstanceKey {
let (result_sender, result_receiver) =
ipc::channel().expect("failed to create IPC channel");
let _ = self.sender.lock().send(Command::GetWebFontInstance(
font_key,
font_size,
font_flags,
result_sender,
));
result_receiver.recv().unwrap()
}
fn get_font_data(&self, identifier: &FontIdentifier) -> Option<Arc<FontData>> {
pub(crate) fn get_font_data(&self, identifier: &FontIdentifier) -> Option<Arc<FontData>> {
self.data_cache.read().get(identifier).cloned()
}
}

View file

@ -8,20 +8,21 @@ use std::io::prelude::*;
use std::path::PathBuf;
use std::sync::atomic::{AtomicI32, Ordering};
use std::sync::Arc;
use std::thread;
use app_units::Au;
use fonts::platform::font::PlatformFont;
use fonts::{
fallback_font_families, FallbackFontSelectionOptions, FontContext, FontData, FontDescriptor,
FontFamilyDescriptor, FontIdentifier, FontSearchScope, FontTemplate, FontTemplateRef,
FontTemplates, PlatformFontMethods, SystemFontServiceProxyTrait,
FontFamilyDescriptor, FontIdentifier, FontSearchScope, FontTemplate, FontTemplateRequestResult,
FontTemplates, PlatformFontMethods, SystemFontServiceMessage, SystemFontServiceProxy,
SystemFontServiceProxySender,
};
use ipc_channel::ipc;
use ipc_channel::ipc::{self, IpcReceiver};
use net_traits::ResourceThreads;
use parking_lot::Mutex;
use servo_arc::Arc as ServoArc;
use servo_atoms::Atom;
use servo_url::ServoUrl;
use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps;
use style::properties::style_structs::Font as FontStyleStruct;
use style::values::computed::font::{
@ -31,15 +32,111 @@ use style::values::computed::font::{
use style::values::computed::{FontLanguageOverride, XLang};
use style::values::generics::font::LineHeight;
use style::ArcSlice;
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, IdNamespace};
use webrender_api::{FontInstanceKey, IdNamespace};
use webrender_traits::WebRenderScriptApi;
struct MockFontCacheThread {
struct TestContext {
context: FontContext,
system_font_service: Arc<MockSystemFontService>,
system_font_service_proxy: SystemFontServiceProxy,
}
impl TestContext {
fn new() -> TestContext {
let (system_font_service, system_font_service_proxy) = MockSystemFontService::spawn();
let (core_sender, _) = ipc::channel().unwrap();
let (storage_sender, _) = ipc::channel().unwrap();
let mock_resource_threads = ResourceThreads::new(core_sender, storage_sender);
let mock_webrender_api = WebRenderScriptApi::dummy();
let proxy_clone = Arc::new(system_font_service_proxy.to_sender().to_proxy());
Self {
context: FontContext::new(proxy_clone, mock_webrender_api, mock_resource_threads),
system_font_service,
system_font_service_proxy,
}
}
}
impl Drop for TestContext {
fn drop(&mut self) {
self.system_font_service_proxy.exit();
}
}
struct MockSystemFontService {
families: Mutex<HashMap<String, FontTemplates>>,
data: Mutex<HashMap<FontIdentifier, Arc<FontData>>>,
find_font_count: AtomicI32,
}
impl MockFontCacheThread {
impl MockSystemFontService {
pub fn spawn() -> (Arc<MockSystemFontService>, SystemFontServiceProxy) {
let (sender, receiver) = ipc::channel().unwrap();
let system_font_service = Arc::new(Self::new());
let system_font_service_clone = system_font_service.clone();
thread::Builder::new()
.name("MockSystemFontService".to_owned())
.spawn(move || system_font_service_clone.run(receiver))
.expect("Thread spawning failed");
(
system_font_service,
SystemFontServiceProxySender(sender).to_proxy(),
)
}
fn run(&self, receiver: IpcReceiver<SystemFontServiceMessage>) {
loop {
match receiver.recv().unwrap() {
SystemFontServiceMessage::GetFontTemplates(
descriptor_to_match,
font_family,
result_sender,
) => {
self.find_font_count.fetch_add(1, Ordering::Relaxed);
let SingleFontFamily::FamilyName(family_name) = font_family else {
let _ = result_sender.send(FontTemplateRequestResult::default());
continue;
};
let templates: Vec<_> = self
.families
.lock()
.get_mut(&*family_name.name)
.map(|family| family.find_for_descriptor(descriptor_to_match.as_ref()))
.unwrap()
.into_iter()
.map(|template| template.borrow().clone())
.collect();
let template_data = templates
.iter()
.map(|template| {
let identifier = template.identifier().clone();
let data = self.data.lock().get(&identifier).unwrap().clone();
(identifier, data)
})
.collect();
let _ = result_sender.send(FontTemplateRequestResult {
templates,
template_data,
});
},
SystemFontServiceMessage::GetFontInstance(_, _, _, result_sender) => {
let _ = result_sender.send(FontInstanceKey(IdNamespace(0), 0));
},
SystemFontServiceMessage::Exit(result_sender) => {
let _ = result_sender.send(());
break;
},
SystemFontServiceMessage::Ping => {},
}
}
}
fn new() -> Self {
let proxy = Self {
families: Default::default(),
@ -69,18 +166,6 @@ impl MockFontCacheThread {
proxy
}
fn identifier_for_font_name(name: &str) -> FontIdentifier {
FontIdentifier::Web(Self::url_for_font_name(name))
}
fn url_for_font_name(name: &str) -> ServoUrl {
let mut path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "tests", "support", "CSSTest"]
.iter()
.collect();
path.push(format!("{}.ttf", name));
ServoUrl::from_file_path(path).unwrap()
}
fn add_face(&self, family: &mut FontTemplates, name: &str) {
let mut path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "tests", "support", "CSSTest"]
.iter()
@ -92,65 +177,20 @@ impl MockFontCacheThread {
file.bytes().map(|b| b.unwrap()).collect(),
));
let url = Self::url_for_font_name(name);
let identifier = FontIdentifier::Web(url.clone());
let identifier = FontIdentifier::Mock(name.into());
let handle =
PlatformFont::new_from_data(identifier.clone(), data.as_arc().clone(), 0, None)
.expect("Could not load test font");
let template =
FontTemplate::new_for_remote_web_font(url, handle.descriptor(), None).unwrap();
family.add_template(template);
family.add_template(FontTemplate::new(
identifier.clone(),
handle.descriptor(),
None,
));
self.data.lock().insert(identifier, data);
}
}
impl SystemFontServiceProxyTrait for MockFontCacheThread {
fn find_matching_font_templates(
&self,
descriptor_to_match: Option<&FontDescriptor>,
font_family: &SingleFontFamily,
) -> Vec<FontTemplateRef> {
self.find_font_count.fetch_add(1, Ordering::Relaxed);
let SingleFontFamily::FamilyName(family_name) = font_family else {
return Vec::new();
};
self.families
.lock()
.get_mut(&*family_name.name)
.map(|family| family.find_for_descriptor(descriptor_to_match))
.unwrap_or_default()
}
fn get_system_font_instance(
&self,
_font_identifier: FontIdentifier,
_size: Au,
_flags: FontInstanceFlags,
) -> FontInstanceKey {
FontInstanceKey(IdNamespace(0), 0)
}
fn get_web_font(&self, _data: Arc<fonts::FontData>, _index: u32) -> FontKey {
FontKey(IdNamespace(0), 0)
}
fn get_web_font_instance(
&self,
_font_key: FontKey,
_size: f32,
_flags: FontInstanceFlags,
) -> FontInstanceKey {
FontInstanceKey(IdNamespace(0), 0)
}
fn get_font_data(&self, identifier: &FontIdentifier) -> Option<Arc<fonts::FontData>> {
self.data.lock().get(identifier).cloned()
}
}
fn style() -> FontStyleStruct {
let mut style = FontStyleStruct {
font_family: FontFamily::serif(),
@ -185,16 +225,9 @@ fn font_family(names: Vec<&str>) -> FontFamily {
}
}
fn mock_resource_threads() -> ResourceThreads {
let (core_sender, _) = ipc::channel().unwrap();
let (storage_sender, _) = ipc::channel().unwrap();
ResourceThreads::new(core_sender, storage_sender)
}
#[test]
fn test_font_group_is_cached_by_style() {
let source = Arc::new(MockFontCacheThread::new());
let context = FontContext::new(source, mock_resource_threads());
let context = TestContext::new();
let style1 = style();
@ -203,16 +236,28 @@ fn test_font_group_is_cached_by_style() {
assert!(
std::ptr::eq(
&*context.font_group(ServoArc::new(style1.clone())).read(),
&*context.font_group(ServoArc::new(style1.clone())).read()
&*context
.context
.font_group(ServoArc::new(style1.clone()))
.read(),
&*context
.context
.font_group(ServoArc::new(style1.clone()))
.read()
),
"the same font group should be returned for two styles with the same hash"
);
assert!(
!std::ptr::eq(
&*context.font_group(ServoArc::new(style1.clone())).read(),
&*context.font_group(ServoArc::new(style2.clone())).read()
&*context
.context
.font_group(ServoArc::new(style1.clone()))
.read(),
&*context
.context
.font_group(ServoArc::new(style2.clone()))
.read()
),
"different font groups should be returned for two styles with different hashes"
)
@ -220,52 +265,60 @@ fn test_font_group_is_cached_by_style() {
#[test]
fn test_font_group_find_by_codepoint() {
let source = Arc::new(MockFontCacheThread::new());
let mut context = FontContext::new(source.clone(), mock_resource_threads());
let mut context = TestContext::new();
let mut style = style();
style.set_font_family(font_family(vec!["CSSTest ASCII", "CSSTest Basic"]));
let group = context.font_group(ServoArc::new(style));
let group = context.context.font_group(ServoArc::new(style));
let font = group
.write()
.find_by_codepoint(&mut context, 'a', None)
.find_by_codepoint(&mut context.context, 'a', None)
.unwrap();
assert_eq!(
font.identifier(),
MockFontCacheThread::identifier_for_font_name("csstest-ascii")
FontIdentifier::Mock("csstest-ascii".into())
);
assert_eq!(
source.find_font_count.fetch_add(0, Ordering::Relaxed),
context
.system_font_service
.find_font_count
.fetch_add(0, Ordering::Relaxed),
1,
"only the first font in the list should have been loaded"
);
let font = group
.write()
.find_by_codepoint(&mut context, 'a', None)
.find_by_codepoint(&mut context.context, 'a', None)
.unwrap();
assert_eq!(
font.identifier(),
MockFontCacheThread::identifier_for_font_name("csstest-ascii")
FontIdentifier::Mock("csstest-ascii".into())
);
assert_eq!(
source.find_font_count.fetch_add(0, Ordering::Relaxed),
context
.system_font_service
.find_font_count
.fetch_add(0, Ordering::Relaxed),
1,
"we shouldn't load the same font a second time"
);
let font = group
.write()
.find_by_codepoint(&mut context, 'á', None)
.find_by_codepoint(&mut context.context, 'á', None)
.unwrap();
assert_eq!(
font.identifier(),
MockFontCacheThread::identifier_for_font_name("csstest-basic-regular")
FontIdentifier::Mock("csstest-basic-regular".into())
);
assert_eq!(
source.find_font_count.fetch_add(0, Ordering::Relaxed),
context
.system_font_service
.find_font_count
.fetch_add(0, Ordering::Relaxed),
2,
"both fonts should now have been loaded"
);
@ -273,39 +326,37 @@ fn test_font_group_find_by_codepoint() {
#[test]
fn test_font_fallback() {
let source = Arc::new(MockFontCacheThread::new());
let mut context = FontContext::new(source, mock_resource_threads());
let mut context = TestContext::new();
let mut style = style();
style.set_font_family(font_family(vec!["CSSTest ASCII"]));
let group = context.font_group(ServoArc::new(style));
let group = context.context.font_group(ServoArc::new(style));
let font = group
.write()
.find_by_codepoint(&mut context, 'a', None)
.find_by_codepoint(&mut context.context, 'a', None)
.unwrap();
assert_eq!(
font.identifier(),
MockFontCacheThread::identifier_for_font_name("csstest-ascii"),
FontIdentifier::Mock("csstest-ascii".into()),
"a family in the group should be used if there is a matching glyph"
);
let font = group
.write()
.find_by_codepoint(&mut context, 'á', None)
.find_by_codepoint(&mut context.context, 'á', None)
.unwrap();
assert_eq!(
font.identifier(),
MockFontCacheThread::identifier_for_font_name("csstest-basic-regular"),
FontIdentifier::Mock("csstest-basic-regular".into()),
"a fallback font should be used if there is no matching glyph in the group"
);
}
#[test]
fn test_font_template_is_cached() {
let source = Arc::new(MockFontCacheThread::new());
let context = FontContext::new(source.clone(), mock_resource_threads());
let context = TestContext::new();
let mut font_descriptor = FontDescriptor {
weight: FontWeight::normal(),
@ -321,14 +372,19 @@ fn test_font_template_is_cached() {
});
let family_descriptor = FontFamilyDescriptor::new(family, FontSearchScope::Any);
let font_template = context.matching_templates(&font_descriptor, &family_descriptor)[0].clone();
let font_template = context
.context
.matching_templates(&font_descriptor, &family_descriptor)[0]
.clone();
let font1 = context
.context
.font(font_template.clone(), &font_descriptor)
.unwrap();
font_descriptor.pt_size = Au(20);
let font2 = context
.context
.font(font_template.clone(), &font_descriptor)
.unwrap();
@ -338,7 +394,10 @@ fn test_font_template_is_cached() {
);
assert_eq!(
source.find_font_count.fetch_add(0, Ordering::Relaxed),
context
.system_font_service
.find_font_count
.fetch_add(0, Ordering::Relaxed),
1,
"we should only have fetched the template data from the cache thread once"
);

View file

@ -11,7 +11,7 @@ use std::thread;
use base::id::PipelineId;
use fnv::FnvHasher;
use fonts::{FontContext, SystemFontServiceProxy};
use fonts::FontContext;
use net_traits::image_cache::{
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
};
@ -24,8 +24,6 @@ use style::context::{RegisteredSpeculativePainter, SharedStyleContext};
use crate::display_list::items::{OpaqueNode, WebRenderImageInfo};
pub type LayoutFontContext = FontContext<SystemFontServiceProxy>;
type WebrenderImageCache =
HashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo, BuildHasherDefault<FnvHasher>>;
@ -44,7 +42,7 @@ pub struct LayoutContext<'a> {
pub image_cache: Arc<dyn ImageCache>,
/// A FontContext to be used during layout.
pub font_context: Arc<FontContext<SystemFontServiceProxy>>,
pub font_context: Arc<FontContext>,
/// A cache of WebRender image info.
pub webrender_image_cache: Arc<RwLock<WebrenderImageCache>>,

View file

@ -11,7 +11,7 @@ use app_units::{Au, MIN_AU};
use base::print_tree::PrintTree;
use bitflags::bitflags;
use euclid::default::{Point2D, Rect, Size2D};
use fonts::FontMetrics;
use fonts::{FontContext, FontMetrics};
use log::debug;
use range::{int_range_index, Range, RangeIndex};
use script_layout_interface::wrapper_traits::PseudoElementType;
@ -33,7 +33,7 @@ use style::values::specified::text::TextOverflowSide;
use unicode_bidi as bidi;
use crate::block::AbsoluteAssignBSizesTraversal;
use crate::context::{LayoutContext, LayoutFontContext};
use crate::context::LayoutContext;
use crate::display_list::items::{DisplayListSection, OpaqueNode};
use crate::display_list::{
BorderPaintingMode, DisplayListBuildState, StackingContextCollectionState,
@ -1239,7 +1239,7 @@ impl InlineFlow {
/// `style` is the style of the block.
pub fn minimum_line_metrics(
&self,
font_context: &LayoutFontContext,
font_context: &FontContext,
style: &ComputedValues,
) -> LineMetrics {
InlineFlow::minimum_line_metrics_for_fragments(
@ -1255,7 +1255,7 @@ impl InlineFlow {
/// `style` is the style of the block that these fragments belong to.
pub fn minimum_line_metrics_for_fragments(
fragments: &[Fragment],
font_context: &LayoutFontContext,
font_context: &FontContext,
style: &ComputedValues,
) -> LineMetrics {
// As a special case, if this flow contains only hypothetical fragments, then the entire

View file

@ -11,7 +11,7 @@ use std::sync::Arc;
use app_units::Au;
use base::text::is_bidi_control;
use fonts::{
self, ByteIndex, FontIdentifier, FontMetrics, FontRef, RunMetrics, ShapingFlags,
self, ByteIndex, FontContext, FontIdentifier, FontMetrics, FontRef, RunMetrics, ShapingFlags,
ShapingOptions, LAST_RESORT_GLYPH_ADVANCE,
};
use log::{debug, warn};
@ -28,7 +28,6 @@ use unicode_bidi as bidi;
use unicode_script::Script;
use xi_unicode::LineBreakLeafIter;
use crate::context::LayoutFontContext;
use crate::fragment::{
Fragment, ScannedTextFlags, ScannedTextFragmentInfo, SpecificFragmentInfo,
UnscannedTextFragmentInfo,
@ -71,7 +70,7 @@ impl TextRunScanner {
pub fn scan_for_runs(
&mut self,
font_context: &LayoutFontContext,
font_context: &FontContext,
mut fragments: LinkedList<Fragment>,
) -> InlineFragments {
debug!(
@ -151,7 +150,7 @@ impl TextRunScanner {
/// be adjusted.
fn flush_clump_to_list(
&mut self,
font_context: &LayoutFontContext,
font_context: &FontContext,
out_fragments: &mut Vec<Fragment>,
paragraph_bytes_processed: &mut usize,
bidi_levels: Option<&[bidi::Level]>,
@ -538,7 +537,7 @@ fn bounding_box_for_run_metrics(
/// Panics if no font can be found for the given font style.
#[inline]
pub fn font_metrics_for_style(
font_context: &LayoutFontContext,
font_context: &FontContext,
style: crate::ServoArc<FontStyleStruct>,
) -> FontMetrics {
let font_group = font_context.font_group(style);

View file

@ -6,7 +6,7 @@ use std::sync::Arc;
use base::id::PipelineId;
use fnv::FnvHashMap;
use fonts::{FontContext, SystemFontServiceProxy};
use fonts::FontContext;
use net_traits::image_cache::{
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
};
@ -27,7 +27,7 @@ pub struct LayoutContext<'a> {
pub style_context: SharedStyleContext<'a>,
/// A FontContext to be used during layout.
pub font_context: Arc<FontContext<SystemFontServiceProxy>>,
pub font_context: Arc<FontContext>,
/// Reference to the script thread image cache.
pub image_cache: Arc<dyn ImageCache>,

View file

@ -8,8 +8,7 @@ use std::ops::Range;
use app_units::Au;
use base::text::is_bidi_control;
use fonts::{
FontContext, FontRef, GlyphRun, ShapingFlags, ShapingOptions, SystemFontServiceProxy,
LAST_RESORT_GLYPH_ADVANCE,
FontContext, FontRef, GlyphRun, ShapingFlags, ShapingOptions, LAST_RESORT_GLYPH_ADVANCE,
};
use fonts_traits::ByteIndex;
use log::warn;
@ -342,7 +341,7 @@ impl TextRun {
pub(super) fn segment_and_shape(
&mut self,
formatting_context_text: &str,
font_context: &FontContext<SystemFontServiceProxy>,
font_context: &FontContext,
linebreaker: &mut LineBreaker,
font_cache: &mut Vec<FontKeyAndMetrics>,
bidi_info: &BidiInfo,
@ -410,7 +409,7 @@ impl TextRun {
fn segment_text_by_font(
&mut self,
formatting_context_text: &str,
font_context: &FontContext<SystemFontServiceProxy>,
font_context: &FontContext,
font_cache: &mut Vec<FontKeyAndMetrics>,
bidi_info: &BidiInfo,
) -> Vec<(TextRunSegment, FontRef)> {
@ -556,7 +555,7 @@ pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec<FontKeyAndMetr
pub(super) fn get_font_for_first_font_for_style(
style: &ComputedValues,
font_context: &FontContext<SystemFontServiceProxy>,
font_context: &FontContext,
) -> Option<FontRef> {
let font = font_context
.font_group(style.clone_font())

View file

@ -134,7 +134,7 @@ pub struct LayoutThread {
image_cache: Arc<dyn ImageCache>,
/// A per-layout FontContext managing font access.
font_context: Arc<FontContext<SystemFontServiceProxy>>,
font_context: Arc<FontContext>,
/// Is this the first reflow in this layout?
first_reflow: Cell<bool>,
@ -577,7 +577,11 @@ impl LayoutThread {
keyword_info: KeywordInfo::medium(),
};
let font_context = Arc::new(FontContext::new(system_font_service, resource_threads));
let font_context = Arc::new(FontContext::new(
system_font_service,
webrender_api.clone(),
resource_threads,
));
let device = Device::new(
MediaType::screen(),
QuirksMode::NoQuirks,

View file

@ -121,7 +121,7 @@ pub struct LayoutThread {
image_cache: Arc<dyn ImageCache>,
/// A FontContext to be used during layout.
font_context: Arc<FontContext<SystemFontServiceProxy>>,
font_context: Arc<FontContext>,
/// Is this the first reflow in this LayoutThread?
first_reflow: Cell<bool>,
@ -521,7 +521,11 @@ impl LayoutThread {
// The device pixel ratio is incorrect (it does not have the hidpi value),
// but it will be set correctly when the initial reflow takes place.
let font_context = Arc::new(FontContext::new(system_font_service, resource_threads));
let font_context = Arc::new(FontContext::new(
system_font_service,
webrender_api_sender.clone(),
resource_threads,
));
let device = Device::new(
MediaType::screen(),
QuirksMode::NoQuirks,
@ -1227,7 +1231,7 @@ impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
}
}
struct LayoutFontMetricsProvider(Arc<FontContext<SystemFontServiceProxy>>);
struct LayoutFontMetricsProvider(Arc<FontContext>);
impl FontMetricsProvider for LayoutFontMetricsProvider {
fn query_font_metrics(

View file

@ -1101,59 +1101,33 @@ impl WebRenderFontApi for WebRenderFontApiCompositorProxy {
flags: FontInstanceFlags,
) -> FontInstanceKey {
let (sender, receiver) = unbounded();
self.0
.send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font(
FontToCompositorMsg::AddFontInstance(font_key, size, flags, sender),
)));
self.0.send(CompositorMsg::Forwarded(
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFontInstance(
font_key, size, flags, sender,
)),
));
receiver.recv().unwrap()
}
fn add_font(&self, data: Arc<IpcSharedMemory>, index: u32) -> FontKey {
let (sender, receiver) = unbounded();
self.0
.send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font(
FontToCompositorMsg::AddFont(sender, index, data),
)));
self.0.send(CompositorMsg::Forwarded(
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFont(
sender, index, data,
)),
));
receiver.recv().unwrap()
}
fn add_system_font(&self, handle: NativeFontHandle) -> FontKey {
let (sender, receiver) = unbounded();
self.0
.send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font(
FontToCompositorMsg::AddSystemFont(sender, handle),
)));
self.0.send(CompositorMsg::Forwarded(
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddSystemFont(
sender, handle,
)),
));
receiver.recv().unwrap()
}
fn forward_add_font_message(
&self,
data: Arc<IpcSharedMemory>,
font_index: u32,
result_sender: IpcSender<FontKey>,
) {
let (sender, receiver) = unbounded();
self.0
.send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font(
FontToCompositorMsg::AddFont(sender, font_index, data),
)));
let _ = result_sender.send(receiver.recv().unwrap());
}
fn forward_add_font_instance_message(
&self,
font_key: FontKey,
size: f32,
flags: FontInstanceFlags,
result_sender: IpcSender<FontInstanceKey>,
) {
let (sender, receiver) = unbounded();
self.0
.send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font(
FontToCompositorMsg::AddFontInstance(font_key, size, flags, sender),
)));
let _ = result_sender.send(receiver.recv().unwrap());
}
}
#[derive(Clone)]

View file

@ -141,7 +141,7 @@ pub struct CompositionPipeline {
pub enum ForwardedToCompositorMsg {
Layout(ScriptToCompositorMsg),
Net(NetToCompositorMsg),
Font(FontToCompositorMsg),
SystemFontService(FontToCompositorMsg),
Canvas(CanvasToCompositorMsg),
}
@ -150,7 +150,9 @@ impl Debug for ForwardedToCompositorMsg {
match self {
ForwardedToCompositorMsg::Layout(_) => write!(f, "Layout(ScriptToCompositorMsg)"),
ForwardedToCompositorMsg::Net(_) => write!(f, "Net(NetToCompositorMsg)"),
ForwardedToCompositorMsg::Font(_) => write!(f, "Font(FontToCompositorMsg)"),
ForwardedToCompositorMsg::SystemFontService(_) => {
write!(f, "SystemFontService(FontToCompositorMsg)")
},
ForwardedToCompositorMsg::Canvas(_) => write!(f, "Canvas(CanvasToCompositorMsg)"),
}
}

View file

@ -192,24 +192,6 @@ pub trait WebRenderFontApi {
) -> FontInstanceKey;
fn add_font(&self, data: Arc<IpcSharedMemory>, index: u32) -> FontKey;
fn add_system_font(&self, handle: NativeFontHandle) -> FontKey;
/// Forward a `AddFont` message, sending it on to the compositor. This is used to get WebRender
/// [`FontKey`]s for web fonts in the per-layout `FontContext`.
fn forward_add_font_message(
&self,
data: Arc<IpcSharedMemory>,
font_index: u32,
result_sender: IpcSender<FontKey>,
);
/// Forward a `AddFontInstance` message, sending it on to the compositor. This is used to get
/// WebRender [`FontInstanceKey`]s for web fonts in the per-layout `FontContext`.
fn forward_add_font_instance_message(
&self,
font_key: FontKey,
size: f32,
flags: FontInstanceFlags,
result_receiver: IpcSender<FontInstanceKey>,
);
}
pub enum CanvasToCompositorMsg {
@ -260,6 +242,8 @@ pub enum ScriptToCompositorMsg {
UpdateImages(Vec<SerializedImageUpdate>),
/// Remove the given font resources from our WebRender instance.
RemoveFonts(Vec<FontKey>, Vec<FontInstanceKey>),
AddFontInstance(FontKey, f32, FontInstanceFlags, IpcSender<FontInstanceKey>),
AddFont(Arc<IpcSharedMemory>, u32, IpcSender<FontKey>),
}
/// A mechanism to send messages from networking to the WebRender instance.
@ -294,11 +278,23 @@ impl WebRenderNetApi {
pub struct WebRenderScriptApi(IpcSender<ScriptToCompositorMsg>);
impl WebRenderScriptApi {
/// Create a new WebrenderIpcSender object that wraps the provided channel sender.
/// Create a new [`WebRenderScriptApi`] object that wraps the provided channel sender.
pub fn new(sender: IpcSender<ScriptToCompositorMsg>) -> Self {
Self(sender)
}
/// Create a new [`WebRenderScriptApi`] object that does not have a listener on the
/// other end.
pub fn dummy() -> Self {
let (sender, _) = ipc::channel().unwrap();
Self::new(sender)
}
/// Get the sender for this proxy.
pub fn sender(&self) -> &IpcSender<ScriptToCompositorMsg> {
&self.0
}
/// Inform WebRender of the existence of this pipeline.
pub fn send_initial_transaction(&self, pipeline: WebRenderPipelineId) {
if let Err(e) = self