aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2024-05-20 16:13:03 +0200
committerGitHub <noreply@github.com>2024-05-20 14:13:03 +0000
commitbe5b527ea34c8be1bfa53d4a4eaf2729e100db28 (patch)
tree371df7732a9c095e268fc3c6a9321672dc515922
parent8d2d955bbb23826b75faf866e4dcccabb8d7f4e8 (diff)
downloadservo-be5b527ea34c8be1bfa53d4a4eaf2729e100db28.tar.gz
servo-be5b527ea34c8be1bfa53d4a4eaf2729e100db28.zip
fonts: Store web fonts in the per-Layout `FontContext` (#32303)
This moves mangement of web fonts to the per-Layout `FontContext`, preventing web fonts from being available in different Documents. Fixes #12920. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
-rw-r--r--Cargo.lock3
-rw-r--r--components/canvas/Cargo.toml1
-rw-r--r--components/canvas/canvas_paint_thread.rs8
-rw-r--r--components/gfx/Cargo.toml1
-rw-r--r--components/gfx/font.rs4
-rw-r--r--components/gfx/font_cache_thread.rs449
-rw-r--r--components/gfx/font_context.rs598
-rw-r--r--components/gfx/font_store.rs130
-rw-r--r--components/gfx/font_template.rs33
-rw-r--r--components/gfx/lib.rs1
-rw-r--r--components/gfx/platform/freetype/android/font_list.rs5
-rw-r--r--components/gfx/platform/freetype/font_list.rs5
-rw-r--r--components/gfx/platform/freetype/ohos/font_list.rs5
-rw-r--r--components/gfx/platform/macos/font_list.rs2
-rw-r--r--components/gfx/platform/windows/font_list.rs2
-rw-r--r--components/gfx/tests/font_context.rs102
-rw-r--r--components/layout_thread/lib.rs30
-rw-r--r--components/layout_thread_2020/lib.rs35
-rw-r--r--components/script/script_thread.rs1
-rw-r--r--components/servo/lib.rs43
-rw-r--r--components/shared/gfx/Cargo.toml1
-rw-r--r--components/shared/net/lib.rs2
-rw-r--r--components/shared/script_layout/lib.rs2
-rw-r--r--components/shared/webrender/lib.rs17
-rw-r--r--tests/wpt/meta/MANIFEST.json29
-rw-r--r--tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document-ref.html17
-rw-r--r--tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document.html28
-rw-r--r--tests/wpt/tests/css/css-fonts/support/iframe-missing-font-face-rule.html2
-rw-r--r--tests/wpt/tests/css/css-fonts/support/iframe-using-ahem-as-web-font.html8
-rw-r--r--tests/wpt/tests/css/css-fonts/support/iframe-without-web-font.html2
30 files changed, 1009 insertions, 557 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9ccee76fa66..a4992641d67 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -639,6 +639,7 @@ dependencies = [
"ipc-channel",
"log",
"lyon_geom",
+ "net_traits",
"num-traits",
"pathfinder_geometry",
"pixels",
@@ -2051,6 +2052,7 @@ dependencies = [
"unicode-bidi",
"unicode-properties",
"unicode-script",
+ "url",
"webrender_api",
"webrender_traits",
"xi-unicode",
@@ -2062,6 +2064,7 @@ dependencies = [
name = "gfx_traits"
version = "0.0.1"
dependencies = [
+ "ipc-channel",
"malloc_size_of",
"malloc_size_of_derive",
"range",
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml
index 46c78b8ea08..3a56aba1024 100644
--- a/components/canvas/Cargo.toml
+++ b/components/canvas/Cargo.toml
@@ -29,6 +29,7 @@ half = "2"
ipc-channel = { workspace = true }
log = { workspace = true }
lyon_geom = "1.0.4"
+net_traits = { workspace = true }
num-traits = { workspace = true }
pathfinder_geometry = "0.5"
pixels = { path = "../pixels" }
diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs
index 63f881ddacf..2ace98e9f1f 100644
--- a/components/canvas/canvas_paint_thread.rs
+++ b/components/canvas/canvas_paint_thread.rs
@@ -16,6 +16,7 @@ use gfx::font_context::FontContext;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use log::warn;
+use net_traits::ResourceThreads;
use webrender_api::ImageKey;
use webrender_traits::ImageUpdate;
@@ -44,12 +45,13 @@ impl<'a> CanvasPaintThread<'a> {
fn new(
webrender_api: Box<dyn WebrenderApi>,
font_cache_thread: FontCacheThread,
+ resource_threads: ResourceThreads,
) -> CanvasPaintThread<'a> {
CanvasPaintThread {
canvases: HashMap::new(),
next_canvas_id: CanvasId(0),
webrender_api,
- font_context: Arc::new(FontContext::new(font_cache_thread)),
+ font_context: Arc::new(FontContext::new(font_cache_thread, resource_threads)),
}
}
@@ -58,6 +60,7 @@ impl<'a> CanvasPaintThread<'a> {
pub fn start(
webrender_api: Box<dyn WebrenderApi + Send>,
font_cache_thread: FontCacheThread,
+ resource_threads: ResourceThreads,
) -> (Sender<ConstellationCanvasMsg>, IpcSender<CanvasMsg>) {
let (ipc_sender, ipc_receiver) = ipc::channel::<CanvasMsg>().unwrap();
let msg_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(ipc_receiver);
@@ -65,7 +68,8 @@ impl<'a> CanvasPaintThread<'a> {
thread::Builder::new()
.name("Canvas".to_owned())
.spawn(move || {
- let mut canvas_paint_thread = CanvasPaintThread::new(webrender_api, font_cache_thread);
+ let mut canvas_paint_thread = CanvasPaintThread::new(
+ webrender_api, font_cache_thread, resource_threads);
loop {
select! {
recv(msg_receiver) -> msg => {
diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml
index e9871643431..ca580f4495f 100644
--- a/components/gfx/Cargo.toml
+++ b/components/gfx/Cargo.toml
@@ -43,6 +43,7 @@ ucd = "0.1.1"
unicode-bidi = { workspace = true, features = ["with_serde"] }
unicode-properties = { workspace = true }
unicode-script = { workspace = true }
+url = { workspace = true }
webrender_api = { workspace = true }
webrender_traits = { workspace = true }
xi-unicode = { workspace = true }
diff --git a/components/gfx/font.rs b/components/gfx/font.rs
index 07db050eff2..97709202a99 100644
--- a/components/gfx/font.rs
+++ b/components/gfx/font.rs
@@ -25,8 +25,8 @@ use style::values::computed::{FontStretch, FontStyle, FontWeight};
use unicode_script::Script;
use webrender_api::{FontInstanceFlags, FontInstanceKey};
-use crate::font_cache_thread::FontIdentifier;
-use crate::font_context::{FontContext, FontSource};
+use crate::font_cache_thread::{FontIdentifier, FontSource};
+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;
diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs
index 12f72ab309d..5be2ed7d492 100644
--- a/components/gfx/font_cache_thread.rs
+++ b/components/gfx/font_cache_thread.rs
@@ -5,34 +5,26 @@
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::ops::{Deref, RangeInclusive};
-use std::sync::{Arc, Mutex};
-use std::{f32, fmt, mem, thread};
+use std::sync::Arc;
+use std::{fmt, thread};
use app_units::Au;
use atomic_refcell::AtomicRefCell;
-use ipc_channel::ipc::{self, IpcBytesSender, IpcReceiver, IpcSender};
-use log::{debug, trace};
+use ipc_channel::ipc::{self, IpcBytesReceiver, IpcBytesSender, IpcReceiver, IpcSender};
+use log::debug;
use malloc_size_of_derive::MallocSizeOf;
-use net_traits::request::{Destination, Referrer, RequestBuilder};
-use net_traits::{fetch_async, CoreResourceThread, FetchResponseMsg};
use serde::{Deserialize, Serialize};
use servo_atoms::Atom;
use servo_url::ServoUrl;
-use style::font_face::{
- FontFaceRuleData, FontFaceSourceFormat, FontFaceSourceFormatKeyword,
- FontStyle as FontFaceStyle, Source,
-};
-use style::media_queries::Device;
-use style::shared_lock::SharedRwLockReadGuard;
-use style::stylesheets::{Stylesheet, StylesheetInDocument};
+use style::font_face::{FontFaceRuleData, FontStyle as FontFaceStyle};
use style::values::computed::font::{FixedPoint, FontStyleFixedPoint};
use style::values::computed::{FontStretch, FontWeight};
use style::values::specified::FontStretch as SpecifiedFontStretch;
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
use webrender_traits::WebRenderFontApi;
-use crate::font::{FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope};
-use crate::font_context::FontSource;
+use crate::font::{FontDescriptor, FontFamilyName};
+use crate::font_store::FontStore;
use crate::font_template::{
FontTemplate, FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods,
};
@@ -41,12 +33,6 @@ use crate::platform::font_list::{
SANS_SERIF_FONT_FAMILY,
};
-/// A list of font templates that make up a given font family.
-#[derive(Default)]
-pub struct FontTemplates {
- templates: Vec<FontTemplateRef>,
-}
-
#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub enum FontIdentifier {
Local(LocalFontIdentifier),
@@ -69,73 +55,12 @@ pub struct SerializedFontTemplate {
bytes_receiver: ipc_channel::ipc::IpcBytesReceiver,
}
-impl FontTemplates {
- /// Find a font in this family that matches a given descriptor.
- pub fn find_for_descriptor(
- &mut self,
- descriptor_to_match: &FontDescriptor,
- ) -> Vec<FontTemplateRef> {
- // TODO(Issue #189): optimize lookup for
- // regular/bold/italic/bolditalic with fixed offsets and a
- // static decision table for fallback between these values.
- let matching_templates: Vec<FontTemplateRef> = self
- .templates
- .iter()
- .filter(|template| template.matches_font_descriptor(descriptor_to_match))
- .cloned()
- .collect();
- if !matching_templates.is_empty() {
- return matching_templates;
- }
-
- // We didn't find an exact match. Do more expensive fuzzy matching.
- // TODO(#190): Do a better job.
- let mut best_templates = Vec::new();
- let mut best_distance = f32::MAX;
- for template in self.templates.iter() {
- let distance = template.descriptor_distance(descriptor_to_match);
- if distance < best_distance {
- best_templates = vec![template.clone()];
- best_distance = distance
- } else if distance == best_distance {
- best_templates.push(template.clone());
- }
- }
-
- if !best_templates.is_empty() {
- return best_templates;
- }
-
- // If a request is made for a font family that exists,
- // pick the first valid font in the family if we failed
- // to find an exact match for the descriptor.
- if let Some(template) = self.templates.first() {
- return vec![template.clone()];
- }
-
- Vec::new()
- }
-
- pub fn add_template(&mut self, new_template: FontTemplate) {
- for existing_template in &self.templates {
- let existing_template = existing_template.borrow();
- if *existing_template.identifier() == new_template.identifier &&
- existing_template.descriptor == new_template.descriptor
- {
- return;
- }
- }
- self.templates
- .push(Arc::new(AtomicRefCell::new(new_template)));
- }
-}
-
/// Commands that the FontContext sends to the font cache thread.
#[derive(Debug, Deserialize, Serialize)]
pub enum Command {
GetFontTemplates(
- FontDescriptor,
- FontFamilyDescriptor,
+ Option<FontDescriptor>,
+ FontFamilyName,
IpcSender<Vec<SerializedFontTemplate>>,
),
GetFontInstance(
@@ -144,8 +69,8 @@ pub enum Command {
FontInstanceFlags,
IpcSender<FontInstanceKey>,
),
- AddWebFont(CSSFontFaceDescriptors, Vec<Source>, IpcSender<()>),
- AddDownloadedWebFont(CSSFontFaceDescriptors, ServoUrl, Vec<u8>, IpcSender<()>),
+ GetWebFont(IpcBytesReceiver, u32, IpcSender<FontKey>),
+ GetWebFontInstance(FontKey, f32, FontInstanceFlags, IpcSender<FontInstanceKey>),
Exit(IpcSender<()>),
Ping,
}
@@ -154,12 +79,9 @@ pub enum Command {
/// font templates that are currently in use.
struct FontCache {
port: IpcReceiver<Command>,
- channel_to_self: IpcSender<Command>,
generic_fonts: HashMap<FontFamilyName, LowercaseString>,
font_data: HashMap<FontIdentifier, Arc<Vec<u8>>>,
- local_families: HashMap<LowercaseString, FontTemplates>,
- web_families: HashMap<LowercaseString, FontTemplates>,
- core_resource_thread: CoreResourceThread,
+ local_families: FontStore,
webrender_api: Box<dyn WebRenderFontApi>,
webrender_fonts: HashMap<FontIdentifier, FontKey>,
font_instances: HashMap<(FontKey, Au), FontInstanceKey>,
@@ -198,9 +120,9 @@ impl FontCache {
let msg = self.port.recv().unwrap();
match msg {
- Command::GetFontTemplates(descriptor_to_match, family_descriptor, result) => {
+ Command::GetFontTemplates(descriptor_to_match, font_family_name, result) => {
let templates =
- self.find_font_templates(&descriptor_to_match, &family_descriptor);
+ self.find_font_templates(descriptor_to_match.as_ref(), &font_family_name);
debug!("Found templates for descriptor {descriptor_to_match:?}: ");
debug!(" {templates:?}");
@@ -238,18 +160,25 @@ impl FontCache {
Command::GetFontInstance(identifier, pt_size, flags, result) => {
let _ = result.send(self.get_font_instance(identifier, pt_size, flags));
},
- Command::AddWebFont(css_font_face_descriptors, sources, result) => {
- self.handle_add_web_font(css_font_face_descriptors, sources, result);
+ Command::GetWebFont(bytes_receiver, font_index, result_sender) => {
+ self.webrender_api.forward_add_font_message(
+ bytes_receiver,
+ font_index,
+ result_sender,
+ );
},
- Command::AddDownloadedWebFont(css_font_face_descriptors, url, data, result) => {
- let family_name = css_font_face_descriptors.family_name.clone();
- let templates = &mut self.web_families.get_mut(&family_name).unwrap();
- if let Ok(template) =
- FontTemplate::new_web_font(url, Arc::new(data), css_font_face_descriptors)
- {
- templates.add_template(template);
- }
- drop(result.send(()));
+ 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) => {
@@ -260,126 +189,11 @@ impl FontCache {
}
}
- fn handle_add_web_font(
- &mut self,
- css_font_face_descriptors: CSSFontFaceDescriptors,
- mut sources: Vec<Source>,
- sender: IpcSender<()>,
- ) {
- let family_name = css_font_face_descriptors.family_name.clone();
- let src = if let Some(src) = sources.pop() {
- src
- } else {
- sender.send(()).unwrap();
- return;
- };
-
- if !self.web_families.contains_key(&family_name) {
- let templates = FontTemplates::default();
- self.web_families.insert(family_name.clone(), templates);
- }
-
- match src {
- Source::Url(url_source) => {
- // https://drafts.csswg.org/css-fonts/#font-fetching-requirements
- let url = match url_source.url.url() {
- Some(url) => url.clone(),
- None => return,
- };
-
- // FIXME:
- // This shouldn't use NoReferrer, but the current documents url
- let request = RequestBuilder::new(url.clone().into(), Referrer::NoReferrer)
- .destination(Destination::Font);
-
- let channel_to_self = self.channel_to_self.clone();
- let bytes = Mutex::new(Vec::new());
- let response_valid = Mutex::new(false);
- debug!("Loading @font-face {} from {}", family_name, url);
- fetch_async(request, &self.core_resource_thread, move |response| {
- match response {
- FetchResponseMsg::ProcessRequestBody |
- FetchResponseMsg::ProcessRequestEOF => (),
- FetchResponseMsg::ProcessResponse(meta_result) => {
- trace!(
- "@font-face {} metadata ok={:?}",
- family_name,
- meta_result.is_ok()
- );
- *response_valid.lock().unwrap() = meta_result.is_ok();
- },
- FetchResponseMsg::ProcessResponseChunk(new_bytes) => {
- trace!("@font-face {} chunk={:?}", family_name, new_bytes);
- if *response_valid.lock().unwrap() {
- bytes.lock().unwrap().extend(new_bytes)
- }
- },
- FetchResponseMsg::ProcessResponseEOF(response) => {
- trace!("@font-face {} EOF={:?}", family_name, response);
- if response.is_err() || !*response_valid.lock().unwrap() {
- let msg = Command::AddWebFont(
- css_font_face_descriptors.clone(),
- sources.clone(),
- sender.clone(),
- );
- channel_to_self.send(msg).unwrap();
- return;
- }
- let bytes = mem::take(&mut *bytes.lock().unwrap());
- trace!("@font-face {} data={:?}", family_name, bytes);
- let bytes = match fontsan::process(&bytes) {
- Ok(san) => san,
- Err(_) => {
- // FIXME(servo/fontsan#1): get an error message
- debug!(
- "Sanitiser rejected web font: \
- family={} url={:?}",
- family_name, url
- );
- let msg = Command::AddWebFont(
- css_font_face_descriptors.clone(),
- sources.clone(),
- sender.clone(),
- );
- channel_to_self.send(msg).unwrap();
- return;
- },
- };
- let command = Command::AddDownloadedWebFont(
- css_font_face_descriptors.clone(),
- url.clone().into(),
- bytes,
- sender.clone(),
- );
- channel_to_self.send(command).unwrap();
- },
- }
- });
- },
- Source::Local(ref font) => {
- let font_face_name = LowercaseString::new(&font.name);
- let templates = &mut self.web_families.get_mut(&family_name).unwrap();
- let mut found = false;
- for_each_variation(&font_face_name, |font_template| {
- found = true;
- templates.add_template(font_template);
- });
- if found {
- sender.send(()).unwrap();
- } else {
- let msg =
- Command::AddWebFont(css_font_face_descriptors.clone(), sources, sender);
- self.channel_to_self.send(msg).unwrap();
- }
- },
- }
- }
-
fn refresh_local_families(&mut self) {
self.local_families.clear();
for_each_available_family(|family_name| {
let family_name = LowercaseString::new(&family_name);
- self.local_families.entry(family_name).or_default();
+ self.local_families.families.entry(family_name).or_default();
});
}
@@ -390,9 +204,9 @@ impl FontCache {
}
}
- fn find_templates_in_local_family(
+ fn find_font_templates(
&mut self,
- descriptor_to_match: &FontDescriptor,
+ descriptor_to_match: Option<&FontDescriptor>,
family_name: &FontFamilyName,
) -> Vec<FontTemplateRef> {
// TODO(Issue #188): look up localized font family names if canonical name not found
@@ -401,6 +215,7 @@ impl FontCache {
// if such family exists, try to match style to a font
let family_name = self.transform_family(family_name);
self.local_families
+ .families
.get_mut(&family_name)
.map(|font_templates| {
if font_templates.templates.is_empty() {
@@ -414,41 +229,13 @@ impl FontCache {
.unwrap_or_default()
}
- fn find_templates_in_web_family(
- &mut self,
- descriptor_to_match: &FontDescriptor,
- family_name: &FontFamilyName,
- ) -> Vec<FontTemplateRef> {
- let family_name = LowercaseString::from(family_name);
- self.web_families
- .get_mut(&family_name)
- .map(|templates| templates.find_for_descriptor(descriptor_to_match))
- .unwrap_or_default()
- }
-
- fn find_font_templates(
- &mut self,
- descriptor_to_match: &FontDescriptor,
- family_descriptor: &FontFamilyDescriptor,
- ) -> Vec<FontTemplateRef> {
- if family_descriptor.scope == FontSearchScope::Any {
- let templates =
- self.find_templates_in_web_family(descriptor_to_match, &family_descriptor.name);
- if !templates.is_empty() {
- return templates;
- }
- }
-
- self.find_templates_in_local_family(descriptor_to_match, &family_descriptor.name)
- }
-
fn get_font_instance(
&mut self,
identifier: FontIdentifier,
pt_size: Au,
flags: FontInstanceFlags,
) -> FontInstanceKey {
- let webrender_api = &self.webrender_api;
+ let webrender_font_api = &self.webrender_api;
let webrender_fonts = &mut self.webrender_fonts;
let font_data = self
.font_data
@@ -466,22 +253,43 @@ impl FontCache {
// this for those platforms.
#[cfg(target_os = "macos")]
if let FontIdentifier::Local(local_font_identifier) = identifier {
- return webrender_api
+ return webrender_font_api
.add_system_font(local_font_identifier.native_font_handle());
}
- webrender_api.add_font(font_data, identifier.index())
+ webrender_font_api.add_font(font_data, identifier.index())
});
*self
.font_instances
.entry((font_key, pt_size))
.or_insert_with(|| {
- webrender_api.add_font_instance(font_key, pt_size.to_f32_px(), flags)
+ webrender_font_api.add_font_instance(font_key, pt_size.to_f32_px(), flags)
})
}
}
+pub trait FontSource: Clone {
+ fn find_matching_font_templates(
+ &self,
+ descriptor_to_match: Option<&FontDescriptor>,
+ font_family_name: &FontFamilyName,
+ ) -> Vec<FontTemplateRef>;
+ fn get_system_font_instance(
+ &self,
+ font_identifier: FontIdentifier,
+ size: Au,
+ flags: FontInstanceFlags,
+ ) -> FontInstanceKey;
+ fn get_web_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey;
+ fn get_web_font_instance(
+ &self,
+ font_key: FontKey,
+ size: f32,
+ flags: FontInstanceFlags,
+ ) -> FontInstanceKey;
+}
+
/// The public interface to the font cache thread, used by per-thread `FontContext` instances (via
/// the `FontSource` trait), and also by layout.
#[derive(Clone, Debug, Deserialize, Serialize)]
@@ -575,13 +383,9 @@ impl From<&FontFaceRuleData> for CSSFontFaceDescriptors {
}
impl FontCacheThread {
- pub fn new(
- core_resource_thread: CoreResourceThread,
- webrender_api: Box<dyn WebRenderFontApi + Send>,
- ) -> FontCacheThread {
+ pub fn new(webrender_api: Box<dyn WebRenderFontApi + Send>) -> FontCacheThread {
let (chan, port) = ipc::channel().unwrap();
- let channel_to_self = chan.clone();
thread::Builder::new()
.name("FontCache".to_owned())
.spawn(move || {
@@ -591,12 +395,9 @@ impl FontCacheThread {
#[allow(clippy::default_constructed_unit_structs)]
let mut cache = FontCache {
port,
- channel_to_self,
generic_fonts,
font_data: HashMap::new(),
- local_families: HashMap::new(),
- web_families: HashMap::new(),
- core_resource_thread,
+ local_families: Default::default(),
webrender_api,
webrender_fonts: HashMap::new(),
font_instances: HashMap::new(),
@@ -610,55 +411,6 @@ impl FontCacheThread {
FontCacheThread { chan }
}
- pub fn add_all_web_fonts_from_stylesheet(
- &self,
- stylesheet: &Stylesheet,
- guard: &SharedRwLockReadGuard,
- device: &Device,
- font_cache_sender: &IpcSender<()>,
- synchronous: bool,
- ) -> usize {
- let (sender, receiver) = if synchronous {
- let (sender, receiver) = ipc::channel().unwrap();
- (Some(sender), Some(receiver))
- } else {
- (None, None)
- };
-
- let mut number_loading = 0;
- stylesheet.effective_font_face_rules(device, guard, |rule| {
- let font_face = match rule.font_face() {
- Some(font_face) => font_face,
- None => return,
- };
-
- let sources: Vec<Source> = font_face
- .sources()
- .0
- .iter()
- .rev()
- .filter(is_supported_web_font_source)
- .cloned()
- .collect();
- if sources.is_empty() {
- return;
- }
-
- let sender = sender.as_ref().unwrap_or(font_cache_sender).clone();
- self.chan
- .send(Command::AddWebFont(rule.into(), sources, sender))
- .unwrap();
-
- // 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
- }
-
pub fn exit(&self) {
let (response_chan, response_port) = ipc::channel().unwrap();
self.chan
@@ -671,8 +423,8 @@ impl FontCacheThread {
}
impl FontSource for FontCacheThread {
- fn get_font_instance(
- &mut self,
+ fn get_system_font_instance(
+ &self,
identifier: FontIdentifier,
size: Au,
flags: FontInstanceFlags,
@@ -700,15 +452,15 @@ impl FontSource for FontCacheThread {
}
fn find_matching_font_templates(
- &mut self,
- descriptor_to_match: &FontDescriptor,
- family_descriptor: FontFamilyDescriptor,
+ &self,
+ descriptor_to_match: Option<&FontDescriptor>,
+ font_family_name: &FontFamilyName,
) -> Vec<FontTemplateRef> {
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
self.chan
.send(Command::GetFontTemplates(
- descriptor_to_match.clone(),
- family_descriptor,
+ descriptor_to_match.cloned(),
+ font_family_name.clone(),
response_chan,
))
.expect("failed to send message to font cache thread");
@@ -736,6 +488,35 @@ impl FontSource for FontCacheThread {
})
.collect()
}
+
+ fn get_web_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey {
+ let (result_sender, result_receiver) =
+ ipc::channel().expect("failed to create IPC channel");
+ let (bytes_sender, bytes_receiver) =
+ ipc::bytes_channel().expect("failed to create IPC channel");
+ let _ = self
+ .chan
+ .send(Command::GetWebFont(bytes_receiver, index, result_sender));
+ let _ = bytes_sender.send(&data);
+ 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.chan.send(Command::GetWebFontInstance(
+ font_key,
+ font_size,
+ font_flags,
+ result_sender,
+ ));
+ result_receiver.recv().unwrap()
+ }
}
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
@@ -771,35 +552,3 @@ impl fmt::Display for LowercaseString {
self.inner.fmt(f)
}
}
-
-fn is_supported_web_font_source(source: &&Source) -> bool {
- let url_source = match source {
- Source::Url(ref url_source) => url_source,
- Source::Local(_) => return true,
- };
- let format_hint = match url_source.format_hint {
- Some(ref format_hint) => format_hint,
- None => return true,
- };
-
- if matches!(
- format_hint,
- FontFaceSourceFormat::Keyword(
- FontFaceSourceFormatKeyword::Truetype |
- FontFaceSourceFormatKeyword::Opentype |
- FontFaceSourceFormatKeyword::Woff |
- FontFaceSourceFormatKeyword::Woff2
- )
- ) {
- return true;
- }
-
- if let FontFaceSourceFormat::String(string) = format_hint {
- return string == "truetype" ||
- string == "opentype" ||
- string == "woff" ||
- string == "woff2";
- }
-
- false
-}
diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs
index 51bd239b8f2..328e54e066c 100644
--- a/components/gfx/font_context.rs
+++ b/components/gfx/font_context.rs
@@ -9,112 +9,72 @@ use std::sync::Arc;
use app_units::Au;
use fnv::FnvHasher;
-use log::debug;
+use ipc_channel::ipc::{self, IpcSender};
+use log::{debug, trace};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use malloc_size_of_derive::MallocSizeOf;
-use parking_lot::{Mutex, RwLock};
+use net_traits::request::{Destination, Referrer, RequestBuilder};
+use net_traits::{fetch_async, CoreResourceThread, FetchResponseMsg, ResourceThreads};
+use parking_lot::{Mutex, ReentrantMutex, RwLock};
use servo_arc::Arc as ServoArc;
use style::computed_values::font_variant_caps::T as FontVariantCaps;
+use style::font_face::{FontFaceSourceFormat, FontFaceSourceFormatKeyword, Source, UrlSource};
+use style::media_queries::Device;
use style::properties::style_structs::Font as FontStyleStruct;
-use webrender_api::{FontInstanceFlags, FontInstanceKey};
-
-use crate::font::{Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef};
-use crate::font_cache_thread::FontIdentifier;
-use crate::font_template::{FontTemplateRef, FontTemplateRefMethods};
+use style::shared_lock::SharedRwLockReadGuard;
+use style::stylesheets::{Stylesheet, StylesheetInDocument};
+use style::Atom;
+use url::Url;
+
+use crate::font::{
+ Font, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontGroup, FontRef, FontSearchScope,
+};
+use crate::font_cache_thread::{
+ CSSFontFaceDescriptors, FontIdentifier, FontSource, LowercaseString,
+};
+use crate::font_store::{CrossThreadFontStore, CrossThreadWebRenderFontStore};
+use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods};
#[cfg(target_os = "macos")]
use crate::platform::core_text_font_cache::CoreTextFontCache;
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
-pub trait FontSource {
- fn get_font_instance(
- &mut self,
- font_identifier: FontIdentifier,
- size: Au,
- flags: FontInstanceFlags,
- ) -> FontInstanceKey;
- fn find_matching_font_templates(
- &mut self,
- descriptor_to_match: &FontDescriptor,
- family_descriptor: FontFamilyDescriptor,
- ) -> Vec<FontTemplateRef>;
-}
-
/// The FontContext represents the per-thread/thread state necessary for
/// working with fonts. It is the public API used by the layout and
/// paint code. It talks directly to the font cache thread where
/// required.
-#[derive(Debug)]
pub struct FontContext<S: FontSource> {
- font_source: Mutex<S>,
-
- // TODO: The font context holds a strong ref to the cached fonts
- // so they will never be released. Find out a good time to drop them.
- // See bug https://github.com/servo/servo/issues/3300
- font_cache: RwLock<HashMap<FontCacheKey, Option<FontRef>>>,
- font_template_cache: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
- font_group_cache:
- RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>,
+ font_source: ReentrantMutex<S>,
+ resource_threads: ReentrantMutex<CoreResourceThread>,
+ cache: CachingFontSource<S>,
+ web_fonts: CrossThreadFontStore,
+ webrender_font_store: CrossThreadWebRenderFontStore,
}
impl<S: FontSource> MallocSizeOf for FontContext<S> {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let font_cache_size = self
- .font_cache
- .read()
- .iter()
- .map(|(key, font)| {
- key.size_of(ops) + font.as_ref().map_or(0, |font| (*font).size_of(ops))
- })
- .sum::<usize>();
- let font_template_cache_size = self
- .font_template_cache
- .read()
- .iter()
- .map(|(key, templates)| {
- let templates_size = templates
- .iter()
- .map(|template| template.borrow().size_of(ops))
- .sum::<usize>();
- key.size_of(ops) + templates_size
- })
- .sum::<usize>();
- let font_group_cache_size = self
- .font_group_cache
- .read()
- .iter()
- .map(|(key, font_group)| key.size_of(ops) + (*font_group.read()).size_of(ops))
- .sum::<usize>();
-
- font_cache_size + font_template_cache_size + font_group_cache_size
+ self.cache.size_of(ops)
}
}
impl<S: FontSource> FontContext<S> {
- pub fn new(font_source: S) -> FontContext<S> {
+ pub fn new(font_source: S, resource_threads: ResourceThreads) -> FontContext<S> {
#[allow(clippy::default_constructed_unit_structs)]
FontContext {
- font_source: Mutex::new(font_source),
- font_cache: RwLock::default(),
- font_template_cache: RwLock::default(),
- font_group_cache: RwLock::default(),
+ font_source: ReentrantMutex::new(font_source.clone()),
+ resource_threads: ReentrantMutex::new(resource_threads.core_thread),
+ cache: CachingFontSource::new(font_source),
+ web_fonts: Arc::new(RwLock::default()),
+ webrender_font_store: Arc::new(RwLock::default()),
}
}
/// Invalidate all caches that this [`FontContext`] holds and any in-process platform-specific
/// caches.
- ///
- /// # Safety
- ///
- /// This should never be called when more than one thread is using the [`FontContext`] or it
- /// may leave the context in an inconsistent state.
pub fn invalidate_caches(&self) {
#[cfg(target_os = "macos")]
CoreTextFontCache::clear_core_text_font_cache();
-
- self.font_cache.write().clear();
- self.font_template_cache.write().clear();
- self.font_group_cache.write().clear();
+ self.cache.invalidate()
}
/// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`.
@@ -132,17 +92,7 @@ impl<S: FontSource> FontContext<S> {
style: ServoArc<FontStyleStruct>,
size: Au,
) -> Arc<RwLock<FontGroup>> {
- let cache_key = FontGroupCacheKey { size, style };
-
- if let Some(font_group) = self.font_group_cache.read().get(&cache_key) {
- return font_group.clone();
- }
-
- let font_group = Arc::new(RwLock::new(FontGroup::new(&cache_key.style)));
- self.font_group_cache
- .write()
- .insert(cache_key, font_group.clone());
- font_group
+ self.cache.font_group_with_size(style, size)
}
/// Returns a font matching the parameters. Fonts are cached, so repeated calls will return a
@@ -187,7 +137,7 @@ impl<S: FontSource> FontContext<S> {
font_descriptor: font_descriptor.clone(),
};
- if let Some(font) = self.font_cache.read().get(&cache_key).cloned() {
+ if let Some(font) = self.cache.fonts.read().get(&cache_key).cloned() {
return font;
}
@@ -203,7 +153,7 @@ impl<S: FontSource> FontContext<S> {
synthesized_small_caps_font,
)
.ok();
- self.font_cache.write().insert(cache_key, font.clone());
+ self.cache.fonts.write().insert(cache_key, font.clone());
font
}
@@ -212,30 +162,23 @@ impl<S: FontSource> FontContext<S> {
descriptor_to_match: &FontDescriptor,
family_descriptor: &FontFamilyDescriptor,
) -> Vec<FontTemplateRef> {
- let cache_key = FontTemplateCacheKey {
- font_descriptor: descriptor_to_match.clone(),
- family_descriptor: family_descriptor.clone(),
- };
-
- if let Some(templates) = self.font_template_cache.read().get(&cache_key).cloned() {
- return templates;
+ // First try to find an appropriate web font that matches this descriptor.
+ if family_descriptor.scope == FontSearchScope::Any {
+ let family_name = LowercaseString::from(&family_descriptor.name);
+ if let Some(templates) = self
+ .web_fonts
+ .read()
+ .families
+ .get(&family_name)
+ .map(|templates| templates.find_for_descriptor(Some(descriptor_to_match)))
+ {
+ return templates;
+ }
}
- debug!(
- "FontContext::font_template cache miss for template_descriptor={:?} family_descriptor={:?}",
- descriptor_to_match,
- family_descriptor
- );
-
- let templates = self
- .font_source
- .lock()
- .find_matching_font_templates(descriptor_to_match, family_descriptor.clone());
-
- self.font_template_cache
- .write()
- .insert(cache_key, templates.clone());
- templates
+ // If not request a matching font from the system font cache.
+ self.cache
+ .matching_templates(descriptor_to_match, family_descriptor)
}
/// Create a `Font` for use in layout calculations, from a `FontTemplateData` returned by the
@@ -251,16 +194,413 @@ impl<S: FontSource> FontContext<S> {
font_descriptor.clone(),
synthesized_small_caps,
)?;
- font.font_key = self.font_source.lock().get_font_instance(
- font_template.identifier(),
- font_descriptor.pt_size,
- font.webrender_font_instance_flags(),
- );
+
+ let font_source = self.font_source.lock();
+ font.font_key = match font_template.identifier() {
+ FontIdentifier::Local(_) => font_source.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(
+ &*font_source,
+ font_template.clone(),
+ font_descriptor.pt_size,
+ font.webrender_font_instance_flags(),
+ ),
+ };
Ok(Arc::new(font))
}
}
+#[derive(Clone)]
+pub struct WebFontDownloadState {
+ css_font_face_descriptors: Arc<CSSFontFaceDescriptors>,
+ remaining_sources: Vec<Source>,
+ result_sender: IpcSender<()>,
+ core_resource_thread: CoreResourceThread,
+ local_fonts: Arc<HashMap<Atom, Option<FontTemplateRef>>>,
+}
+
+pub trait FontContextWebFontMethods {
+ fn add_all_web_fonts_from_stylesheet(
+ &self,
+ stylesheet: &Stylesheet,
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ font_cache_sender: &IpcSender<()>,
+ synchronous: bool,
+ ) -> usize;
+ fn process_next_web_font_source(&self, web_font_download_state: WebFontDownloadState);
+}
+
+impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontContext<S>> {
+ fn add_all_web_fonts_from_stylesheet(
+ &self,
+ stylesheet: &Stylesheet,
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ font_cache_sender: &IpcSender<()>,
+ synchronous: bool,
+ ) -> usize {
+ let (result_sender, receiver) = if synchronous {
+ let (sender, receiver) = ipc::channel().unwrap();
+ (Some(sender), Some(receiver))
+ } else {
+ (None, None)
+ };
+
+ let mut number_loading = 0;
+ stylesheet.effective_font_face_rules(device, guard, |rule| {
+ let font_face = match rule.font_face() {
+ Some(font_face) => font_face,
+ None => return,
+ };
+
+ let sources: Vec<Source> = font_face
+ .sources()
+ .0
+ .iter()
+ .rev()
+ .filter(is_supported_web_font_source)
+ .cloned()
+ .collect();
+ if sources.is_empty() {
+ return;
+ }
+
+ // Fetch all local fonts first, beacause if we try to fetch them later on during the process of
+ // loading the list of web font `src`s we may be running in the context of the router thread, which
+ // means we won't be able to seend IPC messages to the FontCacheThread.
+ //
+ // TODO: This is completely wrong. The specification says that `local()` font-family should match
+ // against full PostScript names, but this is matching against font family names. This works...
+ // sometimes.
+ let mut local_fonts = HashMap::new();
+ for source in sources.iter() {
+ if let Source::Local(family_name) = source {
+ local_fonts
+ .entry(family_name.name.clone())
+ .or_insert_with(|| {
+ let family_name = FontFamilyName::Specific(family_name.name.clone());
+ self.font_source
+ .lock()
+ .find_matching_font_templates(None, &family_name)
+ .first()
+ .cloned()
+ });
+ }
+ }
+
+ let result_sender = result_sender.as_ref().unwrap_or(font_cache_sender).clone();
+ self.process_next_web_font_source(WebFontDownloadState {
+ css_font_face_descriptors: Arc::new(rule.into()),
+ remaining_sources: sources,
+ result_sender,
+ core_resource_thread: self.resource_threads.lock().clone(),
+ 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
+ }
+
+ fn process_next_web_font_source(&self, mut state: WebFontDownloadState) {
+ let Some(source) = state.remaining_sources.pop() else {
+ state.result_sender.send(()).unwrap();
+ return;
+ };
+
+ let this = self.clone();
+ let web_font_family_name = state.css_font_face_descriptors.family_name.clone();
+ match source {
+ Source::Url(url_source) => {
+ RemoteWebFontDownloader::download(url_source, this, web_font_family_name, state)
+ },
+ Source::Local(ref local_family_name) => {
+ if let Some(new_template) = state
+ .local_fonts
+ .get(&local_family_name.name)
+ .cloned()
+ .flatten()
+ .and_then(|local_template| {
+ FontTemplate::new_for_local_web_font(
+ local_template,
+ &state.css_font_face_descriptors,
+ )
+ .ok()
+ })
+ {
+ this.web_fonts
+ .write()
+ .families
+ .entry(web_font_family_name.clone())
+ .or_default()
+ .add_template(new_template);
+ drop(state.result_sender.send(()));
+ } else {
+ this.process_next_web_font_source(state);
+ }
+ },
+ }
+ }
+}
+
+struct RemoteWebFontDownloader<FCT: FontSource> {
+ font_context: Arc<FontContext<FCT>>,
+ url: ServoArc<Url>,
+ web_font_family_name: LowercaseString,
+ response_valid: Mutex<bool>,
+ response_data: Mutex<Vec<u8>>,
+}
+
+enum DownloaderResponseResult {
+ InProcess,
+ Finished,
+ Failure,
+}
+
+impl<FCT: FontSource + Send + 'static> RemoteWebFontDownloader<FCT> {
+ fn download(
+ url_source: UrlSource,
+ font_context: Arc<FontContext<FCT>>,
+ web_font_family_name: LowercaseString,
+ state: WebFontDownloadState,
+ ) {
+ // https://drafts.csswg.org/css-fonts/#font-fetching-requirements
+ let url = match url_source.url.url() {
+ Some(url) => url.clone(),
+ None => return,
+ };
+
+ // FIXME: This shouldn't use NoReferrer, but the current documents url
+ let request = RequestBuilder::new(url.clone().into(), Referrer::NoReferrer)
+ .destination(Destination::Font);
+
+ debug!("Loading @font-face {} from {}", web_font_family_name, url);
+ let downloader = Self {
+ font_context,
+ url,
+ web_font_family_name,
+ response_valid: Mutex::new(false),
+ response_data: Mutex::default(),
+ };
+
+ let core_resource_thread_clone = state.core_resource_thread.clone();
+ fetch_async(
+ request,
+ &core_resource_thread_clone,
+ move |response_message| match downloader.handle_web_font_fetch_message(response_message)
+ {
+ DownloaderResponseResult::InProcess => {},
+ DownloaderResponseResult::Finished => {
+ if !downloader.process_downloaded_font_and_signal_completion(&state) {
+ downloader
+ .font_context
+ .process_next_web_font_source(state.clone())
+ }
+ },
+ DownloaderResponseResult::Failure => downloader
+ .font_context
+ .process_next_web_font_source(state.clone()),
+ },
+ )
+ }
+
+ /// After a download finishes, try to process the downloaded data, returning true if
+ /// the font is added successfully to the [`FontContext`] or false if it isn't.
+ fn process_downloaded_font_and_signal_completion(&self, state: &WebFontDownloadState) -> bool {
+ let font_data = std::mem::take(&mut *self.response_data.lock());
+ trace!(
+ "@font-face {} data={:?}",
+ self.web_font_family_name,
+ font_data
+ );
+
+ let font_data = match fontsan::process(&font_data) {
+ Ok(bytes) => bytes,
+ Err(error) => {
+ debug!(
+ "Sanitiser rejected web font: family={} url={:?} with {error:?}",
+ self.web_font_family_name, self.url,
+ );
+ return false;
+ },
+ };
+
+ let Ok(new_template) = FontTemplate::new_for_remote_web_font(
+ self.url.clone().into(),
+ Arc::new(font_data),
+ &state.css_font_face_descriptors,
+ ) else {
+ return false;
+ };
+
+ let family_name = state.css_font_face_descriptors.family_name.clone();
+ self.font_context
+ .web_fonts
+ .write()
+ .families
+ .entry(family_name.clone())
+ .or_default()
+ .add_template(new_template);
+
+ // Signal the Document that we have finished trying to load this web font.
+ drop(state.result_sender.send(()));
+ true
+ }
+
+ fn handle_web_font_fetch_message(
+ &self,
+ response_message: FetchResponseMsg,
+ ) -> DownloaderResponseResult {
+ match response_message {
+ FetchResponseMsg::ProcessRequestBody | FetchResponseMsg::ProcessRequestEOF => {
+ DownloaderResponseResult::InProcess
+ },
+ FetchResponseMsg::ProcessResponse(meta_result) => {
+ trace!(
+ "@font-face {} metadata ok={:?}",
+ self.web_font_family_name,
+ meta_result.is_ok()
+ );
+ *self.response_valid.lock() = meta_result.is_ok();
+ DownloaderResponseResult::InProcess
+ },
+ FetchResponseMsg::ProcessResponseChunk(new_bytes) => {
+ trace!(
+ "@font-face {} chunk={:?}",
+ self.web_font_family_name,
+ new_bytes
+ );
+ if *self.response_valid.lock() {
+ self.response_data.lock().extend(new_bytes)
+ }
+ DownloaderResponseResult::InProcess
+ },
+ FetchResponseMsg::ProcessResponseEOF(response) => {
+ trace!(
+ "@font-face {} EOF={:?}",
+ self.web_font_family_name,
+ response
+ );
+ if response.is_err() || !*self.response_valid.lock() {
+ return DownloaderResponseResult::Failure;
+ }
+ DownloaderResponseResult::Finished
+ },
+ }
+ }
+}
+
+#[derive(Default)]
+pub struct CachingFontSource<FCT: FontSource> {
+ font_cache_thread: ReentrantMutex<FCT>,
+ fonts: RwLock<HashMap<FontCacheKey, Option<FontRef>>>,
+ templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
+ resolved_font_groups:
+ RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>,
+}
+
+impl<FCT: FontSource> CachingFontSource<FCT> {
+ fn new(font_cache_thread: FCT) -> Self {
+ Self {
+ font_cache_thread: ReentrantMutex::new(font_cache_thread),
+ fonts: Default::default(),
+ templates: Default::default(),
+ resolved_font_groups: Default::default(),
+ }
+ }
+
+ fn invalidate(&self) {
+ self.fonts.write().clear();
+ self.templates.write().clear();
+ self.resolved_font_groups.write().clear();
+ }
+
+ pub fn matching_templates(
+ &self,
+ descriptor_to_match: &FontDescriptor,
+ family_descriptor: &FontFamilyDescriptor,
+ ) -> Vec<FontTemplateRef> {
+ let cache_key = FontTemplateCacheKey {
+ font_descriptor: descriptor_to_match.clone(),
+ family_descriptor: family_descriptor.clone(),
+ };
+ if let Some(templates) = self.templates.read().get(&cache_key).cloned() {
+ return templates;
+ }
+
+ debug!(
+ "CachingFontSource: cache miss for template_descriptor={:?} family_descriptor={:?}",
+ descriptor_to_match, family_descriptor
+ );
+ let templates = self
+ .font_cache_thread
+ .lock()
+ .find_matching_font_templates(Some(descriptor_to_match), &family_descriptor.name);
+ self.templates.write().insert(cache_key, templates.clone());
+
+ templates
+ }
+
+ pub fn font_group_with_size(
+ &self,
+ style: ServoArc<FontStyleStruct>,
+ size: Au,
+ ) -> Arc<RwLock<FontGroup>> {
+ let cache_key = FontGroupCacheKey { size, style };
+ if let Some(font_group) = self.resolved_font_groups.read().get(&cache_key) {
+ return font_group.clone();
+ }
+ let font_group = Arc::new(RwLock::new(FontGroup::new(&cache_key.style)));
+ self.resolved_font_groups
+ .write()
+ .insert(cache_key, font_group.clone());
+ font_group
+ }
+}
+
+impl<FCT: FontSource> MallocSizeOf for CachingFontSource<FCT> {
+ fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
+ let font_cache_size = self
+ .fonts
+ .read()
+ .iter()
+ .map(|(key, font)| {
+ key.size_of(ops) + font.as_ref().map_or(0, |font| (*font).size_of(ops))
+ })
+ .sum::<usize>();
+ let font_template_cache_size = self
+ .templates
+ .read()
+ .iter()
+ .map(|(key, templates)| {
+ let templates_size = templates
+ .iter()
+ .map(|template| template.borrow().size_of(ops))
+ .sum::<usize>();
+ key.size_of(ops) + templates_size
+ })
+ .sum::<usize>();
+ let font_group_cache_size = self
+ .resolved_font_groups
+ .read()
+ .iter()
+ .map(|(key, font_group)| key.size_of(ops) + (*font_group.read()).size_of(ops))
+ .sum::<usize>();
+
+ font_cache_size + font_template_cache_size + font_group_cache_size
+ }
+}
+
#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
struct FontCacheKey {
font_identifier: FontIdentifier,
@@ -296,3 +636,35 @@ impl Hash for FontGroupCacheKey {
self.style.hash.hash(hasher)
}
}
+
+fn is_supported_web_font_source(source: &&Source) -> bool {
+ let url_source = match source {
+ Source::Url(ref url_source) => url_source,
+ Source::Local(_) => return true,
+ };
+ let format_hint = match url_source.format_hint {
+ Some(ref format_hint) => format_hint,
+ None => return true,
+ };
+
+ if matches!(
+ format_hint,
+ FontFaceSourceFormat::Keyword(
+ FontFaceSourceFormatKeyword::Truetype |
+ FontFaceSourceFormatKeyword::Opentype |
+ FontFaceSourceFormatKeyword::Woff |
+ FontFaceSourceFormatKeyword::Woff2
+ )
+ ) {
+ return true;
+ }
+
+ if let FontFaceSourceFormat::String(string) = format_hint {
+ return string == "truetype" ||
+ string == "opentype" ||
+ string == "woff" ||
+ string == "woff2";
+ }
+
+ false
+}
diff --git a/components/gfx/font_store.rs b/components/gfx/font_store.rs
new file mode 100644
index 00000000000..888a0ae0a1e
--- /dev/null
+++ b/components/gfx/font_store.rs
@@ -0,0 +1,130 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use std::collections::HashMap;
+use std::sync::Arc;
+
+use app_units::Au;
+use atomic_refcell::AtomicRefCell;
+use parking_lot::RwLock;
+use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
+
+use crate::font::FontDescriptor;
+use crate::font_cache_thread::{FontIdentifier, FontSource, LowercaseString};
+use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods};
+
+#[derive(Default)]
+pub struct FontStore {
+ pub(crate) families: HashMap<LowercaseString, FontTemplates>,
+}
+pub(crate) type CrossThreadFontStore = Arc<RwLock<FontStore>>;
+
+impl FontStore {
+ pub(crate) fn clear(&mut self) {
+ self.families.clear();
+ }
+}
+
+#[derive(Default)]
+pub struct WebRenderFontStore {
+ pub(crate) webrender_font_key_map: HashMap<FontIdentifier, FontKey>,
+ pub(crate) webrender_font_instance_map: HashMap<(FontKey, Au), FontInstanceKey>,
+}
+pub(crate) type CrossThreadWebRenderFontStore = Arc<RwLock<WebRenderFontStore>>;
+
+impl WebRenderFontStore {
+ pub(crate) fn get_font_instance<FCT: FontSource>(
+ &mut self,
+ font_cache_thread: &FCT,
+ font_template: FontTemplateRef,
+ pt_size: Au,
+ flags: FontInstanceFlags,
+ ) -> FontInstanceKey {
+ let webrender_font_key_map = &mut self.webrender_font_key_map;
+ let identifier = font_template.identifier().clone();
+ let font_key = *webrender_font_key_map
+ .entry(identifier.clone())
+ .or_insert_with(|| {
+ font_cache_thread.get_web_font(font_template.data(), identifier.index())
+ });
+
+ *self
+ .webrender_font_instance_map
+ .entry((font_key, pt_size))
+ .or_insert_with(|| {
+ font_cache_thread.get_web_font_instance(font_key, pt_size.to_f32_px(), flags)
+ })
+ }
+}
+
+/// A list of font templates that make up a given font family.
+#[derive(Clone, Debug, Default)]
+pub struct FontTemplates {
+ pub(crate) templates: Vec<FontTemplateRef>,
+}
+
+impl FontTemplates {
+ /// Find a font in this family that matches a given descriptor.
+ pub fn find_for_descriptor(
+ &self,
+ descriptor_to_match: Option<&FontDescriptor>,
+ ) -> Vec<FontTemplateRef> {
+ let Some(descriptor_to_match) = descriptor_to_match else {
+ return self.templates.clone();
+ };
+
+ // TODO(Issue #189): optimize lookup for
+ // regular/bold/italic/bolditalic with fixed offsets and a
+ // static decision table for fallback between these values.
+ let matching_templates: Vec<FontTemplateRef> = self
+ .templates
+ .iter()
+ .filter(|template| template.matches_font_descriptor(descriptor_to_match))
+ .cloned()
+ .collect();
+ if !matching_templates.is_empty() {
+ return matching_templates;
+ }
+
+ // We didn't find an exact match. Do more expensive fuzzy matching.
+ // TODO(#190): Do a better job.
+ let mut best_templates = Vec::new();
+ let mut best_distance = f32::MAX;
+ for template in self.templates.iter() {
+ let distance = template.descriptor_distance(descriptor_to_match);
+ if distance < best_distance {
+ best_templates = vec![template.clone()];
+ best_distance = distance
+ } else if distance == best_distance {
+ best_templates.push(template.clone());
+ }
+ }
+
+ if !best_templates.is_empty() {
+ return best_templates;
+ }
+
+ // If a request is made for a font family that exists,
+ // pick the first valid font in the family if we failed
+ // to find an exact match for the descriptor.
+ if let Some(template) = self.templates.first() {
+ return vec![template.clone()];
+ }
+
+ Vec::new()
+ }
+
+ pub fn add_template(&mut self, new_template: FontTemplate) {
+ for existing_template in &self.templates {
+ let existing_template = existing_template.borrow();
+ if *existing_template.identifier() == new_template.identifier &&
+ existing_template.descriptor == new_template.descriptor
+ {
+ return;
+ }
+ }
+ self.templates
+ .push(Arc::new(AtomicRefCell::new(new_template)));
+ }
+}
diff --git a/components/gfx/font_template.rs b/components/gfx/font_template.rs
index 67f98b17c98..3bc9f21acf0 100644
--- a/components/gfx/font_template.rs
+++ b/components/gfx/font_template.rs
@@ -100,7 +100,7 @@ impl FontTemplateDescriptor {
fn override_values_with_css_font_template_descriptors(
&mut self,
- css_font_template_descriptors: CSSFontFaceDescriptors,
+ css_font_template_descriptors: &CSSFontFaceDescriptors,
) {
if let Some(weight) = css_font_template_descriptors.weight {
self.weight = weight;
@@ -117,8 +117,8 @@ impl FontTemplateDescriptor {
if let Some(stretch) = css_font_template_descriptors.stretch {
self.stretch = stretch;
}
- if let Some(unicode_range) = css_font_template_descriptors.unicode_range {
- self.unicode_range = Some(unicode_range);
+ if let Some(ref unicode_range) = css_font_template_descriptors.unicode_range {
+ self.unicode_range = Some(unicode_range.clone());
}
}
}
@@ -126,6 +126,7 @@ impl FontTemplateDescriptor {
/// This describes all the information needed to create
/// font instance handles. It contains a unique
/// FontTemplateData structure that is platform specific.
+#[derive(Clone)]
pub struct FontTemplate {
pub identifier: FontIdentifier,
pub descriptor: FontTemplateDescriptor,
@@ -154,7 +155,8 @@ impl Debug for FontTemplate {
/// is common, regardless of the number of instances of
/// this font handle per thread.
impl FontTemplate {
- pub fn new_local(
+ /// Create a new [`FontTemplate`] for a system font installed locally.
+ pub fn new_for_local_font(
identifier: LocalFontIdentifier,
descriptor: FontTemplateDescriptor,
) -> FontTemplate {
@@ -165,10 +167,11 @@ impl FontTemplate {
}
}
- pub fn new_web_font(
+ /// Create a new [`FontTemplate`] for a `@font-family` with a `url(...)` `src` font.
+ pub fn new_for_remote_web_font(
url: ServoUrl,
data: Arc<Vec<u8>>,
- css_font_template_descriptors: CSSFontFaceDescriptors,
+ css_font_template_descriptors: &CSSFontFaceDescriptors,
) -> Result<FontTemplate, &'static str> {
let identifier = FontIdentifier::Web(url.clone());
let Ok(handle) = PlatformFont::new_from_data(identifier, data.clone(), 0, None) else {
@@ -185,6 +188,20 @@ impl FontTemplate {
})
}
+ /// Create a new [`FontTemplate`] for a `@font-family` with a `local(...)` `src`. This takes in
+ /// the template of the local font and creates a new one that reflects the properties specified
+ /// by `@font-family` in the stylesheet.
+ pub fn new_for_local_web_font(
+ local_template: FontTemplateRef,
+ css_font_template_descriptors: &CSSFontFaceDescriptors,
+ ) -> Result<FontTemplate, &'static str> {
+ let mut alias_template = local_template.borrow().clone();
+ alias_template
+ .descriptor
+ .override_values_with_css_font_template_descriptors(css_font_template_descriptors);
+ Ok(alias_template)
+ }
+
pub fn identifier(&self) -> &FontIdentifier {
&self.identifier
}
@@ -233,6 +250,10 @@ impl FontTemplateRefMethods for FontTemplateRef {
}
fn data(&self) -> Arc<Vec<u8>> {
+ if let Some(data) = self.borrow().data.clone() {
+ return data;
+ }
+
let mut template = self.borrow_mut();
let identifier = template.identifier.clone();
template
diff --git a/components/gfx/lib.rs b/components/gfx/lib.rs
index 4840918fe1f..694cbfc006a 100644
--- a/components/gfx/lib.rs
+++ b/components/gfx/lib.rs
@@ -7,6 +7,7 @@
pub mod font;
pub mod font_cache_thread;
pub mod font_context;
+pub mod font_store;
pub mod font_template;
#[allow(unsafe_code)]
pub mod platform;
diff --git a/components/gfx/platform/freetype/android/font_list.rs b/components/gfx/platform/freetype/android/font_list.rs
index 6e6b45c5ad7..4f24d677889 100644
--- a/components/gfx/platform/freetype/android/font_list.rs
+++ b/components/gfx/platform/freetype/android/font_list.rs
@@ -486,7 +486,10 @@ where
None => StyleFontStyle::NORMAL,
};
let descriptor = FontTemplateDescriptor::new(weight, stretch, style);
- callback(FontTemplate::new_local(local_font_identifier, descriptor));
+ callback(FontTemplate::new_for_local_font(
+ local_font_identifier,
+ descriptor,
+ ));
};
if let Some(family) = FONT_LIST.find_family(family_name) {
diff --git a/components/gfx/platform/freetype/font_list.rs b/components/gfx/platform/freetype/font_list.rs
index 00db8e6855e..78ea0a2ad50 100644
--- a/components/gfx/platform/freetype/font_list.rs
+++ b/components/gfx/platform/freetype/font_list.rs
@@ -152,7 +152,10 @@ where
};
let descriptor = FontTemplateDescriptor::new(weight, stretch, style);
- callback(FontTemplate::new_local(local_font_identifier, descriptor))
+ callback(FontTemplate::new_for_local_font(
+ local_font_identifier,
+ descriptor,
+ ))
}
FcFontSetDestroy(matches);
diff --git a/components/gfx/platform/freetype/ohos/font_list.rs b/components/gfx/platform/freetype/ohos/font_list.rs
index 95d7209c6ad..a9a21d63b7b 100644
--- a/components/gfx/platform/freetype/ohos/font_list.rs
+++ b/components/gfx/platform/freetype/ohos/font_list.rs
@@ -158,7 +158,10 @@ where
stretch,
style,
};
- callback(FontTemplate::new_local(local_font_identifier, descriptor));
+ callback(FontTemplate::new_for_local_font(
+ local_font_identifier,
+ descriptor,
+ ));
};
if let Some(family) = FONT_LIST.find_family(family_name) {
diff --git a/components/gfx/platform/macos/font_list.rs b/components/gfx/platform/macos/font_list.rs
index 272916e2d07..faeb54af0d4 100644
--- a/components/gfx/platform/macos/font_list.rs
+++ b/components/gfx/platform/macos/font_list.rs
@@ -81,7 +81,7 @@ where
postscript_name: Atom::from(family_descriptor.font_name()),
path: Atom::from(path),
};
- callback(FontTemplate::new_local(identifier, descriptor));
+ callback(FontTemplate::new_for_local_font(identifier, descriptor));
}
}
}
diff --git a/components/gfx/platform/windows/font_list.rs b/components/gfx/platform/windows/font_list.rs
index 9cfdbfd988e..5d1f076d6f9 100644
--- a/components/gfx/platform/windows/font_list.rs
+++ b/components/gfx/platform/windows/font_list.rs
@@ -81,7 +81,7 @@ where
let local_font_identifier = LocalFontIdentifier {
font_descriptor: Arc::new(font.to_descriptor()),
};
- callback(FontTemplate::new_local(
+ callback(FontTemplate::new_for_local_font(
local_font_identifier,
template_descriptor,
))
diff --git a/components/gfx/tests/font_context.rs b/components/gfx/tests/font_context.rs
index 54babf3ef83..06870f6f88c 100644
--- a/components/gfx/tests/font_context.rs
+++ b/components/gfx/tests/font_context.rs
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use std::cell::Cell;
+use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
@@ -13,9 +13,12 @@ use app_units::Au;
use gfx::font::{
fallback_font_families, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope,
};
-use gfx::font_cache_thread::{CSSFontFaceDescriptors, FontIdentifier, FontTemplates};
-use gfx::font_context::{FontContext, FontSource};
+use gfx::font_cache_thread::{CSSFontFaceDescriptors, FontIdentifier, FontSource};
+use gfx::font_context::FontContext;
+use gfx::font_store::FontTemplates;
use gfx::font_template::{FontTemplate, FontTemplateRef};
+use ipc_channel::ipc;
+use net_traits::ResourceThreads;
use servo_arc::Arc;
use servo_atoms::Atom;
use servo_url::ServoUrl;
@@ -27,15 +30,16 @@ use style::values::computed::font::{
};
use style::values::computed::{FontLanguageOverride, XLang};
use style::values::generics::font::LineHeight;
-use webrender_api::{FontInstanceFlags, FontInstanceKey, IdNamespace};
+use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, IdNamespace};
-struct TestFontSource {
- families: HashMap<String, FontTemplates>,
+#[derive(Clone)]
+struct MockFontCacheThread {
+ families: RefCell<HashMap<String, FontTemplates>>,
find_font_count: Rc<Cell<isize>>,
}
-impl TestFontSource {
- fn new() -> TestFontSource {
+impl MockFontCacheThread {
+ fn new() -> MockFontCacheThread {
let mut csstest_ascii = FontTemplates::default();
Self::add_face(&mut csstest_ascii, "csstest-ascii");
@@ -50,8 +54,8 @@ impl TestFontSource {
families.insert("CSSTest Basic".to_owned(), csstest_basic);
families.insert(fallback_font_families(None)[0].to_owned(), fallback);
- TestFontSource {
- families,
+ MockFontCacheThread {
+ families: RefCell::new(families),
find_font_count: Rc::new(Cell::new(0)),
}
}
@@ -77,37 +81,51 @@ impl TestFontSource {
let file = File::open(path).unwrap();
let data: Vec<u8> = file.bytes().map(|b| b.unwrap()).collect();
family.add_template(
- FontTemplate::new_web_font(
+ FontTemplate::new_for_remote_web_font(
Self::url_for_font_name(name),
std::sync::Arc::new(data),
- CSSFontFaceDescriptors::new(name),
+ &CSSFontFaceDescriptors::new(name),
)
.unwrap(),
);
}
}
-impl FontSource for TestFontSource {
- fn get_font_instance(
- &mut self,
- _font_identifier: FontIdentifier,
- _size: Au,
- _flags: FontInstanceFlags,
- ) -> FontInstanceKey {
- FontInstanceKey(IdNamespace(0), 0)
- }
-
+impl FontSource for MockFontCacheThread {
fn find_matching_font_templates(
- &mut self,
- descriptor_to_match: &FontDescriptor,
- family_descriptor: FontFamilyDescriptor,
+ &self,
+ descriptor_to_match: Option<&FontDescriptor>,
+ font_family_name: &FontFamilyName,
) -> Vec<FontTemplateRef> {
self.find_font_count.set(self.find_font_count.get() + 1);
self.families
- .get_mut(family_descriptor.name())
+ .borrow_mut()
+ .get_mut(font_family_name.name())
.map(|family| family.find_for_descriptor(descriptor_to_match))
.unwrap_or_default()
}
+
+ fn get_system_font_instance(
+ &self,
+ _: FontIdentifier,
+ _: Au,
+ _: FontInstanceFlags,
+ ) -> FontInstanceKey {
+ FontInstanceKey(IdNamespace(0), 0)
+ }
+
+ fn get_web_font(&self, _: std::sync::Arc<Vec<u8>>, _: u32) -> webrender_api::FontKey {
+ FontKey(IdNamespace(0), 0)
+ }
+
+ fn get_web_font_instance(
+ &self,
+ _: webrender_api::FontKey,
+ _: f32,
+ _: FontInstanceFlags,
+ ) -> FontInstanceKey {
+ FontInstanceKey(IdNamespace(0), 0)
+ }
}
fn style() -> FontStyleStruct {
@@ -147,10 +165,16 @@ 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 = TestFontSource::new();
- let context = FontContext::new(source);
+ let source = MockFontCacheThread::new();
+ let context = FontContext::new(source, mock_resource_threads());
let style1 = style();
@@ -176,9 +200,9 @@ fn test_font_group_is_cached_by_style() {
#[test]
fn test_font_group_find_by_codepoint() {
- let source = TestFontSource::new();
+ let source = MockFontCacheThread::new();
let count = source.find_font_count.clone();
- let mut context = FontContext::new(source);
+ let mut context = FontContext::new(source, mock_resource_threads());
let mut style = style();
style.set_font_family(font_family(vec!["CSSTest ASCII", "CSSTest Basic"]));
@@ -188,7 +212,7 @@ fn test_font_group_find_by_codepoint() {
let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
assert_eq!(
font.identifier(),
- TestFontSource::identifier_for_font_name("csstest-ascii")
+ MockFontCacheThread::identifier_for_font_name("csstest-ascii")
);
assert_eq!(
count.get(),
@@ -199,7 +223,7 @@ fn test_font_group_find_by_codepoint() {
let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
assert_eq!(
font.identifier(),
- TestFontSource::identifier_for_font_name("csstest-ascii")
+ MockFontCacheThread::identifier_for_font_name("csstest-ascii")
);
assert_eq!(
count.get(),
@@ -210,15 +234,15 @@ fn test_font_group_find_by_codepoint() {
let font = group.write().find_by_codepoint(&mut context, 'á').unwrap();
assert_eq!(
font.identifier(),
- TestFontSource::identifier_for_font_name("csstest-basic-regular")
+ MockFontCacheThread::identifier_for_font_name("csstest-basic-regular")
);
assert_eq!(count.get(), 2, "both fonts should now have been loaded");
}
#[test]
fn test_font_fallback() {
- let source = TestFontSource::new();
- let mut context = FontContext::new(source);
+ let source = MockFontCacheThread::new();
+ let mut context = FontContext::new(source, mock_resource_threads());
let mut style = style();
style.set_font_family(font_family(vec!["CSSTest ASCII"]));
@@ -228,23 +252,23 @@ fn test_font_fallback() {
let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
assert_eq!(
font.identifier(),
- TestFontSource::identifier_for_font_name("csstest-ascii"),
+ MockFontCacheThread::identifier_for_font_name("csstest-ascii"),
"a family in the group should be used if there is a matching glyph"
);
let font = group.write().find_by_codepoint(&mut context, 'á').unwrap();
assert_eq!(
font.identifier(),
- TestFontSource::identifier_for_font_name("csstest-basic-regular"),
+ MockFontCacheThread::identifier_for_font_name("csstest-basic-regular"),
"a fallback font should be used if there is no matching glyph in the group"
);
}
#[test]
fn test_font_template_is_cached() {
- let source = TestFontSource::new();
+ let source = MockFontCacheThread::new();
let count = source.find_font_count.clone();
- let context = FontContext::new(source);
+ let context = FontContext::new(source, mock_resource_threads());
let mut font_descriptor = FontDescriptor {
weight: FontWeight::normal(),
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs
index 8cb612bd1de..22cbb52fc6a 100644
--- a/components/layout_thread/lib.rs
+++ b/components/layout_thread/lib.rs
@@ -26,7 +26,7 @@ use fnv::FnvHashMap;
use fxhash::{FxHashMap, FxHashSet};
use gfx::font;
use gfx::font_cache_thread::FontCacheThread;
-use gfx::font_context::FontContext;
+use gfx::font_context::{FontContext, FontContextWebFontMethods};
use histogram::Histogram;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
@@ -54,6 +54,7 @@ use log::{debug, error, trace, warn};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
use net_traits::image_cache::{ImageCache, UsePlaceholder};
+use net_traits::ResourceThreads;
use parking_lot::RwLock;
use profile_traits::mem::{Report, ReportKind};
use profile_traits::path;
@@ -133,13 +134,10 @@ pub struct LayoutThread {
/// Reference to the script thread image cache.
image_cache: Arc<dyn ImageCache>,
- /// Public interface to the font cache thread.
- font_cache_thread: FontCacheThread,
-
- /// A FontContext to be used during layout.
+ /// A FontContext tFontCacheThreadImplg layout.
font_context: Arc<FontContext<FontCacheThread>>,
- /// Is this the first reflow in this LayoutThread?
+ /// Is this the first reflow iFontCacheThreadImplread?
first_reflow: Cell<bool>,
/// Flag to indicate whether to use parallel operations
@@ -207,6 +205,7 @@ impl LayoutFactory for LayoutFactoryImpl {
config.constellation_chan,
config.script_chan,
config.image_cache,
+ config.resource_threads,
config.font_cache_thread,
config.time_profiler_chan,
config.webrender_api_sender,
@@ -566,6 +565,7 @@ impl LayoutThread {
constellation_chan: IpcSender<ConstellationMsg>,
script_chan: IpcSender<ConstellationControlMsg>,
image_cache: Arc<dyn ImageCache>,
+ resource_threads: ResourceThreads,
font_cache_thread: FontCacheThread,
time_profiler_chan: profile_time::ProfilerChan,
webrender_api: WebRenderScriptApi,
@@ -575,7 +575,7 @@ impl LayoutThread {
// Let webrender know about this pipeline by sending an empty display list.
webrender_api.send_initial_transaction(id.into());
- let font_context = Arc::new(FontContext::new(font_cache_thread.clone()));
+ let font_context = Arc::new(FontContext::new(font_cache_thread, resource_threads));
let device = Device::new(
MediaType::screen(),
QuirksMode::NoQuirks,
@@ -604,7 +604,6 @@ impl LayoutThread {
time_profiler_chan,
registered_painters: RegisteredPaintersImpl(Default::default()),
image_cache,
- font_cache_thread,
font_context,
first_reflow: Cell::new(true),
font_cache_sender: ipc_font_cache_sender,
@@ -711,14 +710,13 @@ impl LayoutThread {
// Find all font-face rules and notify the font cache of them.
// GWTODO: Need to handle unloading web fonts.
if stylesheet.is_effective_for_device(self.stylist.device(), guard) {
- let newly_loading_font_count =
- self.font_cache_thread.add_all_web_fonts_from_stylesheet(
- stylesheet,
- guard,
- self.stylist.device(),
- &self.font_cache_sender,
- self.debug.load_webfonts_synchronously,
- );
+ 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
diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs
index 4dd245a4925..a482f8592c1 100644
--- a/components/layout_thread_2020/lib.rs
+++ b/components/layout_thread_2020/lib.rs
@@ -10,6 +10,7 @@
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
+use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
use std::process;
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -24,7 +25,7 @@ use euclid::{Point2D, Scale, Size2D, Vector2D};
use fnv::FnvHashMap;
use fxhash::FxHashMap;
use gfx::font_cache_thread::FontCacheThread;
-use gfx::font_context::FontContext;
+use gfx::font_context::{FontContext, FontContextWebFontMethods};
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use layout::context::LayoutContext;
@@ -41,6 +42,7 @@ use log::{debug, error, warn};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
use net_traits::image_cache::{ImageCache, UsePlaceholder};
+use net_traits::ResourceThreads;
use parking_lot::RwLock;
use profile_traits::mem::{Report, ReportKind};
use profile_traits::path;
@@ -120,10 +122,6 @@ pub struct LayoutThread {
/// Reference to the script thread image cache.
image_cache: Arc<dyn ImageCache>,
- /// Public interface to the font cache thread. This needs to be behind a [`ReentrantMutex`],
- /// because some font cache operations can trigger others.
- font_cache_thread: FontCacheThread,
-
/// A FontContext to be used during layout.
font_context: Arc<FontContext<FontCacheThread>>,
@@ -183,6 +181,7 @@ impl LayoutFactory for LayoutFactoryImpl {
config.constellation_chan,
config.script_chan,
config.image_cache,
+ config.resource_threads,
config.font_cache_thread,
config.time_profiler_chan,
config.webrender_api_sender,
@@ -483,6 +482,7 @@ impl LayoutThread {
constellation_chan: IpcSender<ConstellationMsg>,
script_chan: IpcSender<ConstellationControlMsg>,
image_cache: Arc<dyn ImageCache>,
+ resource_threads: ResourceThreads,
font_cache_thread: FontCacheThread,
time_profiler_chan: profile_time::ProfilerChan,
webrender_api_sender: WebRenderScriptApi,
@@ -494,7 +494,7 @@ 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(font_cache_thread.clone()));
+ let font_context = Arc::new(FontContext::new(font_cache_thread, resource_threads));
let device = Device::new(
MediaType::screen(),
QuirksMode::NoQuirks,
@@ -523,7 +523,6 @@ impl LayoutThread {
time_profiler_chan,
registered_painters: RegisteredPaintersImpl(Default::default()),
image_cache,
- font_cache_thread,
font_context,
first_reflow: Cell::new(true),
font_cache_sender: ipc_font_cache_sender,
@@ -627,14 +626,13 @@ impl LayoutThread {
// Find all font-face rules and notify the font cache of them.
// GWTODO: Need to handle unloading web fonts.
if stylesheet.is_effective_for_device(self.stylist.device(), guard) {
- let newly_loading_font_count =
- self.font_cache_thread.add_all_web_fonts_from_stylesheet(
- stylesheet,
- guard,
- self.stylist.device(),
- &self.font_cache_sender,
- self.debug.load_webfonts_synchronously,
- );
+ 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
@@ -1243,7 +1241,6 @@ impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
}
}
-#[derive(Debug)]
struct LayoutFontMetricsProvider(Arc<FontContext<FontCacheThread>>);
impl FontMetricsProvider for LayoutFontMetricsProvider {
@@ -1307,3 +1304,9 @@ impl FontMetricsProvider for LayoutFontMetricsProvider {
}
}
}
+
+impl Debug for LayoutFontMetricsProvider {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_tuple("LayoutFontMetricsProvider").finish()
+ }
+}
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 7a3401a3eac..99ed7b8dbe0 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -3536,6 +3536,7 @@ impl ScriptThread {
script_chan: self.control_chan.clone(),
image_cache: self.image_cache.clone(),
font_cache_thread: self.font_cache_thread.clone(),
+ resource_threads: self.resource_threads.clone(),
time_profiler_chan: self.time_profiler_chan.clone(),
webrender_api_sender: self.webrender_api_sender.clone(),
paint_time_metrics,
diff --git a/components/servo/lib.rs b/components/servo/lib.rs
index 755538acf06..533abeeba73 100644
--- a/components/servo/lib.rs
+++ b/components/servo/lib.rs
@@ -72,7 +72,6 @@ use ipc_channel::ipc::{self, IpcSender};
use log::{error, trace, warn, Log, Metadata, Record};
use media::{GLPlayerThreads, GlApi, NativeDisplay, WindowGLContext};
use net::resource_thread::new_resource_threads;
-use net_traits::IpcSend;
use profile::{mem as profile_mem, time as profile_time};
use profile_traits::{mem, time};
use script::serviceworker_manager::ServiceWorkerManager;
@@ -996,14 +995,14 @@ fn create_constellation(
opts.ignore_certificate_errors,
);
- let font_cache_thread = FontCacheThread::new(
- public_resource_threads.sender(),
- Box::new(FontCacheWR(compositor_proxy.clone())),
- );
+ let font_cache_thread = FontCacheThread::new(Box::new(WebRenderFontApiCompositorProxy(
+ compositor_proxy.clone(),
+ )));
let (canvas_create_sender, canvas_ipc_sender) = CanvasPaintThread::start(
Box::new(CanvasWebrenderApi(compositor_proxy.clone())),
font_cache_thread.clone(),
+ public_resource_threads.clone(),
);
let initial_state = InitialConstellationState {
@@ -1049,9 +1048,9 @@ fn create_constellation(
)
}
-struct FontCacheWR(CompositorProxy);
+struct WebRenderFontApiCompositorProxy(CompositorProxy);
-impl WebRenderFontApi for FontCacheWR {
+impl WebRenderFontApi for WebRenderFontApiCompositorProxy {
fn add_font_instance(
&self,
font_key: FontKey,
@@ -1065,6 +1064,7 @@ impl WebRenderFontApi for FontCacheWR {
)));
receiver.recv().unwrap()
}
+
fn add_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey {
let (sender, receiver) = unbounded();
let (bytes_sender, bytes_receiver) =
@@ -1085,6 +1085,35 @@ impl WebRenderFontApi for FontCacheWR {
)));
receiver.recv().unwrap()
}
+
+ fn forward_add_font_message(
+ &self,
+ bytes_receiver: ipc::IpcBytesReceiver,
+ font_index: u32,
+ result_sender: IpcSender<FontKey>,
+ ) {
+ let (sender, receiver) = unbounded();
+ self.0
+ .send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font(
+ FontToCompositorMsg::AddFont(sender, font_index, bytes_receiver),
+ )));
+ 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)]
diff --git a/components/shared/gfx/Cargo.toml b/components/shared/gfx/Cargo.toml
index 23bf75bfb07..292cd58b92c 100644
--- a/components/shared/gfx/Cargo.toml
+++ b/components/shared/gfx/Cargo.toml
@@ -11,6 +11,7 @@ name = "gfx_traits"
path = "lib.rs"
[dependencies]
+ipc-channel = { workspace = true }
malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
range = { path = "../../range" }
diff --git a/components/shared/net/lib.rs b/components/shared/net/lib.rs
index 595d47c7b06..1fccc8a18c4 100644
--- a/components/shared/net/lib.rs
+++ b/components/shared/net/lib.rs
@@ -341,7 +341,7 @@ where
// See also: https://github.com/servo/servo/blob/735480/components/script/script_thread.rs#L313
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ResourceThreads {
- core_thread: CoreResourceThread,
+ pub core_thread: CoreResourceThread,
storage_thread: IpcSender<StorageThreadMsg>,
}
diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs
index d07a009af0e..a15f0be9bde 100644
--- a/components/shared/script_layout/lib.rs
+++ b/components/shared/script_layout/lib.rs
@@ -29,6 +29,7 @@ use libc::c_void;
use malloc_size_of_derive::MallocSizeOf;
use metrics::PaintTimeMetrics;
use net_traits::image_cache::{ImageCache, PendingImageId};
+use net_traits::ResourceThreads;
use profile_traits::mem::Report;
use profile_traits::time;
use script_traits::{
@@ -164,6 +165,7 @@ pub struct LayoutConfig {
pub constellation_chan: IpcSender<LayoutMsg>,
pub script_chan: IpcSender<ConstellationControlMsg>,
pub image_cache: Arc<dyn ImageCache>,
+ pub resource_threads: ResourceThreads,
pub font_cache_thread: FontCacheThread,
pub time_profiler_chan: time::ProfilerChan,
pub webrender_api_sender: WebRenderScriptApi,
diff --git a/components/shared/webrender/lib.rs b/components/shared/webrender/lib.rs
index d4883d150e4..2eb252e37a4 100644
--- a/components/shared/webrender/lib.rs
+++ b/components/shared/webrender/lib.rs
@@ -188,6 +188,23 @@ pub trait WebRenderFontApi {
flags: FontInstanceFlags,
) -> FontInstanceKey;
fn add_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey;
+ /// Forward an already prepared `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,
+ bytes_receiver: IpcBytesReceiver,
+ font_index: u32,
+ result_sender: IpcSender<FontKey>,
+ );
+ /// Forward an already prepared `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>,
+ );
fn add_system_font(&self, handle: NativeFontHandle) -> FontKey;
}
diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json
index a08d09d6500..3fc382143d9 100644
--- a/tests/wpt/meta/MANIFEST.json
+++ b/tests/wpt/meta/MANIFEST.json
@@ -174727,6 +174727,19 @@
{}
]
],
+ "downloadable-font-scoped-to-document.html": [
+ "2dbc350069cd9c61925967655f83217b800fc9eb",
+ [
+ null,
+ [
+ [
+ "/css/css-fonts/downloadable-font-scoped-to-document-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
"first-available-font-001.html": [
"5eb88f7bf6713b80e0adb4728681e62c3f2dc2bc",
[
@@ -396276,6 +396289,10 @@
"28a92a86dcaf6bc9c45bb75fce4869bc0ae21c37",
[]
],
+ "downloadable-font-scoped-to-document-ref.html": [
+ "4d7da060cbf1a161aa1366f0bcb00b94e09502ad",
+ []
+ ],
"first-available-font-001-ref.html": [
"0acbd338e0ce9f558d2eaa2e48ad4be0524fb0ae",
[]
@@ -403399,6 +403416,18 @@
"6ed4aa506e95a35a065318f597547653bda52eb5",
[]
],
+ "iframe-missing-font-face-rule.html": [
+ "da97b781e8072923138e0160320e76d3013c3e53",
+ []
+ ],
+ "iframe-using-ahem-as-web-font.html": [
+ "b21066df8f57a8b11432a1168a62e7a4fbbe07b1",
+ []
+ ],
+ "iframe-without-web-font.html": [
+ "85e7fef282889894016088e082b623b92a436784",
+ []
+ ],
"js": {
"font-variant-features.js": [
"4b56fee193956710b847ba79c5f9c3a5a7d15a33",
diff --git a/tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document-ref.html b/tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document-ref.html
new file mode 100644
index 00000000000..4d7da060cbf
--- /dev/null
+++ b/tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document-ref.html
@@ -0,0 +1,17 @@
+ <!DOCTYPE html>
+
+<html>
+ <head>
+ <title>CSS fonts: Web fonts loaded in a document are not available in other documents</title>
+ <link rel="author" title="Martin Robinson" href="mrobinson@igalia.com">
+ <link rel="author" title="Mukilan Thiyagarajan" href="mukilan@igalia.com">
+ </head>
+
+ <body>
+ <p>Test passes if Ahem is only used in the first iframe.</p>
+ <iframe src="support/iframe-using-ahem-as-web-font.html"></iframe>
+ <iframe src="support/iframe-without-web-font.html"></iframe>
+ </body>
+
+</html>
+
diff --git a/tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document.html b/tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document.html
new file mode 100644
index 00000000000..2dbc350069c
--- /dev/null
+++ b/tests/wpt/tests/css/css-fonts/downloadable-font-scoped-to-document.html
@@ -0,0 +1,28 @@
+ <!DOCTYPE html>
+
+<html class="reftest-wait">
+ <head>
+ <title>CSS fonts: Web fonts loaded in a document are not available in other documents</title>
+ <link rel="author" title="Martin Robinson" href="mrobinson@igalia.com">
+ <link rel="author" title="Mukilan Thiyagarajan" href="mukilan@igalia.com">
+ <link rel="match" href="downloadable-font-scoped-to-document-ref.html">
+ <link rel="help" href="https://drafts.csswg.org/css-fonts/#font-face-rule">
+ </head>
+
+ <body>
+ <p>Test passes if Ahem is only used in the first iframe.</p>
+ <iframe id="iframe1" src="support/iframe-using-ahem-as-web-font.html"></iframe>
+ <iframe id="iframe2" src=""></iframe>
+
+ <script>
+ // Delay the loading of the second iframe to make it more likely that the font
+ // has loaded properly into the first iframe.
+ iframe1.onload = () => {
+ iframe2.src ="support/iframe-missing-font-face-rule.html";
+ document.documentElement.classList.remove('reftest-wait');
+ };
+ </script>
+ </body>
+
+</html>
+
diff --git a/tests/wpt/tests/css/css-fonts/support/iframe-missing-font-face-rule.html b/tests/wpt/tests/css/css-fonts/support/iframe-missing-font-face-rule.html
new file mode 100644
index 00000000000..da97b781e80
--- /dev/null
+++ b/tests/wpt/tests/css/css-fonts/support/iframe-missing-font-face-rule.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="font-size: 30px; font-family: CustomFontFamily">Hello!</div>
diff --git a/tests/wpt/tests/css/css-fonts/support/iframe-using-ahem-as-web-font.html b/tests/wpt/tests/css/css-fonts/support/iframe-using-ahem-as-web-font.html
new file mode 100644
index 00000000000..b21066df8f5
--- /dev/null
+++ b/tests/wpt/tests/css/css-fonts/support/iframe-using-ahem-as-web-font.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<style>
+@font-face {
+ font-family: CustomFontFamily;
+ src: url(/fonts/Ahem.ttf);
+}
+</style>
+<div style="font-size: 30px; font-family: CustomFontFamily">Hello!</div>
diff --git a/tests/wpt/tests/css/css-fonts/support/iframe-without-web-font.html b/tests/wpt/tests/css/css-fonts/support/iframe-without-web-font.html
new file mode 100644
index 00000000000..85e7fef2828
--- /dev/null
+++ b/tests/wpt/tests/css/css-fonts/support/iframe-without-web-font.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="font-size: 30px;">Hello!</div>