mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
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>
This commit is contained in:
parent
8d2d955bbb
commit
be5b527ea3
30 changed files with 1010 additions and 558 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -639,6 +639,7 @@ dependencies = [
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
"log",
|
"log",
|
||||||
"lyon_geom",
|
"lyon_geom",
|
||||||
|
"net_traits",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"pathfinder_geometry",
|
"pathfinder_geometry",
|
||||||
"pixels",
|
"pixels",
|
||||||
|
@ -2051,6 +2052,7 @@ dependencies = [
|
||||||
"unicode-bidi",
|
"unicode-bidi",
|
||||||
"unicode-properties",
|
"unicode-properties",
|
||||||
"unicode-script",
|
"unicode-script",
|
||||||
|
"url",
|
||||||
"webrender_api",
|
"webrender_api",
|
||||||
"webrender_traits",
|
"webrender_traits",
|
||||||
"xi-unicode",
|
"xi-unicode",
|
||||||
|
@ -2062,6 +2064,7 @@ dependencies = [
|
||||||
name = "gfx_traits"
|
name = "gfx_traits"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ipc-channel",
|
||||||
"malloc_size_of",
|
"malloc_size_of",
|
||||||
"malloc_size_of_derive",
|
"malloc_size_of_derive",
|
||||||
"range",
|
"range",
|
||||||
|
|
|
@ -29,6 +29,7 @@ half = "2"
|
||||||
ipc-channel = { workspace = true }
|
ipc-channel = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
lyon_geom = "1.0.4"
|
lyon_geom = "1.0.4"
|
||||||
|
net_traits = { workspace = true }
|
||||||
num-traits = { workspace = true }
|
num-traits = { workspace = true }
|
||||||
pathfinder_geometry = "0.5"
|
pathfinder_geometry = "0.5"
|
||||||
pixels = { path = "../pixels" }
|
pixels = { path = "../pixels" }
|
||||||
|
|
|
@ -16,6 +16,7 @@ use gfx::font_context::FontContext;
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
use net_traits::ResourceThreads;
|
||||||
use webrender_api::ImageKey;
|
use webrender_api::ImageKey;
|
||||||
use webrender_traits::ImageUpdate;
|
use webrender_traits::ImageUpdate;
|
||||||
|
|
||||||
|
@ -44,12 +45,13 @@ impl<'a> CanvasPaintThread<'a> {
|
||||||
fn new(
|
fn new(
|
||||||
webrender_api: Box<dyn WebrenderApi>,
|
webrender_api: Box<dyn WebrenderApi>,
|
||||||
font_cache_thread: FontCacheThread,
|
font_cache_thread: FontCacheThread,
|
||||||
|
resource_threads: ResourceThreads,
|
||||||
) -> CanvasPaintThread<'a> {
|
) -> CanvasPaintThread<'a> {
|
||||||
CanvasPaintThread {
|
CanvasPaintThread {
|
||||||
canvases: HashMap::new(),
|
canvases: HashMap::new(),
|
||||||
next_canvas_id: CanvasId(0),
|
next_canvas_id: CanvasId(0),
|
||||||
webrender_api,
|
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(
|
pub fn start(
|
||||||
webrender_api: Box<dyn WebrenderApi + Send>,
|
webrender_api: Box<dyn WebrenderApi + Send>,
|
||||||
font_cache_thread: FontCacheThread,
|
font_cache_thread: FontCacheThread,
|
||||||
|
resource_threads: ResourceThreads,
|
||||||
) -> (Sender<ConstellationCanvasMsg>, IpcSender<CanvasMsg>) {
|
) -> (Sender<ConstellationCanvasMsg>, IpcSender<CanvasMsg>) {
|
||||||
let (ipc_sender, ipc_receiver) = ipc::channel::<CanvasMsg>().unwrap();
|
let (ipc_sender, ipc_receiver) = ipc::channel::<CanvasMsg>().unwrap();
|
||||||
let msg_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(ipc_receiver);
|
let msg_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(ipc_receiver);
|
||||||
|
@ -65,7 +68,8 @@ impl<'a> CanvasPaintThread<'a> {
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("Canvas".to_owned())
|
.name("Canvas".to_owned())
|
||||||
.spawn(move || {
|
.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 {
|
loop {
|
||||||
select! {
|
select! {
|
||||||
recv(msg_receiver) -> msg => {
|
recv(msg_receiver) -> msg => {
|
||||||
|
|
|
@ -43,6 +43,7 @@ ucd = "0.1.1"
|
||||||
unicode-bidi = { workspace = true, features = ["with_serde"] }
|
unicode-bidi = { workspace = true, features = ["with_serde"] }
|
||||||
unicode-properties = { workspace = true }
|
unicode-properties = { workspace = true }
|
||||||
unicode-script = { workspace = true }
|
unicode-script = { workspace = true }
|
||||||
|
url = { workspace = true }
|
||||||
webrender_api = { workspace = true }
|
webrender_api = { workspace = true }
|
||||||
webrender_traits = { workspace = true }
|
webrender_traits = { workspace = true }
|
||||||
xi-unicode = { workspace = true }
|
xi-unicode = { workspace = true }
|
||||||
|
|
|
@ -25,8 +25,8 @@ use style::values::computed::{FontStretch, FontStyle, FontWeight};
|
||||||
use unicode_script::Script;
|
use unicode_script::Script;
|
||||||
use webrender_api::{FontInstanceFlags, FontInstanceKey};
|
use webrender_api::{FontInstanceFlags, FontInstanceKey};
|
||||||
|
|
||||||
use crate::font_cache_thread::FontIdentifier;
|
use crate::font_cache_thread::{FontIdentifier, FontSource};
|
||||||
use crate::font_context::{FontContext, FontSource};
|
use crate::font_context::FontContext;
|
||||||
use crate::font_template::{FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods};
|
use crate::font_template::{FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods};
|
||||||
use crate::platform::font::{FontTable, PlatformFont};
|
use crate::platform::font::{FontTable, PlatformFont};
|
||||||
pub use crate::platform::font_list::fallback_font_families;
|
pub use crate::platform::font_list::fallback_font_families;
|
||||||
|
|
|
@ -5,34 +5,26 @@
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::{Deref, RangeInclusive};
|
use std::ops::{Deref, RangeInclusive};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::Arc;
|
||||||
use std::{f32, fmt, mem, thread};
|
use std::{fmt, thread};
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use atomic_refcell::AtomicRefCell;
|
use atomic_refcell::AtomicRefCell;
|
||||||
use ipc_channel::ipc::{self, IpcBytesSender, IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{self, IpcBytesReceiver, IpcBytesSender, IpcReceiver, IpcSender};
|
||||||
use log::{debug, trace};
|
use log::debug;
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
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 serde::{Deserialize, Serialize};
|
||||||
use servo_atoms::Atom;
|
use servo_atoms::Atom;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use style::font_face::{
|
use style::font_face::{FontFaceRuleData, FontStyle as FontFaceStyle};
|
||||||
FontFaceRuleData, FontFaceSourceFormat, FontFaceSourceFormatKeyword,
|
|
||||||
FontStyle as FontFaceStyle, Source,
|
|
||||||
};
|
|
||||||
use style::media_queries::Device;
|
|
||||||
use style::shared_lock::SharedRwLockReadGuard;
|
|
||||||
use style::stylesheets::{Stylesheet, StylesheetInDocument};
|
|
||||||
use style::values::computed::font::{FixedPoint, FontStyleFixedPoint};
|
use style::values::computed::font::{FixedPoint, FontStyleFixedPoint};
|
||||||
use style::values::computed::{FontStretch, FontWeight};
|
use style::values::computed::{FontStretch, FontWeight};
|
||||||
use style::values::specified::FontStretch as SpecifiedFontStretch;
|
use style::values::specified::FontStretch as SpecifiedFontStretch;
|
||||||
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
|
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
|
||||||
use webrender_traits::WebRenderFontApi;
|
use webrender_traits::WebRenderFontApi;
|
||||||
|
|
||||||
use crate::font::{FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope};
|
use crate::font::{FontDescriptor, FontFamilyName};
|
||||||
use crate::font_context::FontSource;
|
use crate::font_store::FontStore;
|
||||||
use crate::font_template::{
|
use crate::font_template::{
|
||||||
FontTemplate, FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods,
|
FontTemplate, FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods,
|
||||||
};
|
};
|
||||||
|
@ -41,12 +33,6 @@ use crate::platform::font_list::{
|
||||||
SANS_SERIF_FONT_FAMILY,
|
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)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
|
||||||
pub enum FontIdentifier {
|
pub enum FontIdentifier {
|
||||||
Local(LocalFontIdentifier),
|
Local(LocalFontIdentifier),
|
||||||
|
@ -69,73 +55,12 @@ pub struct SerializedFontTemplate {
|
||||||
bytes_receiver: ipc_channel::ipc::IpcBytesReceiver,
|
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.
|
/// Commands that the FontContext sends to the font cache thread.
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
GetFontTemplates(
|
GetFontTemplates(
|
||||||
FontDescriptor,
|
Option<FontDescriptor>,
|
||||||
FontFamilyDescriptor,
|
FontFamilyName,
|
||||||
IpcSender<Vec<SerializedFontTemplate>>,
|
IpcSender<Vec<SerializedFontTemplate>>,
|
||||||
),
|
),
|
||||||
GetFontInstance(
|
GetFontInstance(
|
||||||
|
@ -144,8 +69,8 @@ pub enum Command {
|
||||||
FontInstanceFlags,
|
FontInstanceFlags,
|
||||||
IpcSender<FontInstanceKey>,
|
IpcSender<FontInstanceKey>,
|
||||||
),
|
),
|
||||||
AddWebFont(CSSFontFaceDescriptors, Vec<Source>, IpcSender<()>),
|
GetWebFont(IpcBytesReceiver, u32, IpcSender<FontKey>),
|
||||||
AddDownloadedWebFont(CSSFontFaceDescriptors, ServoUrl, Vec<u8>, IpcSender<()>),
|
GetWebFontInstance(FontKey, f32, FontInstanceFlags, IpcSender<FontInstanceKey>),
|
||||||
Exit(IpcSender<()>),
|
Exit(IpcSender<()>),
|
||||||
Ping,
|
Ping,
|
||||||
}
|
}
|
||||||
|
@ -154,12 +79,9 @@ pub enum Command {
|
||||||
/// font templates that are currently in use.
|
/// font templates that are currently in use.
|
||||||
struct FontCache {
|
struct FontCache {
|
||||||
port: IpcReceiver<Command>,
|
port: IpcReceiver<Command>,
|
||||||
channel_to_self: IpcSender<Command>,
|
|
||||||
generic_fonts: HashMap<FontFamilyName, LowercaseString>,
|
generic_fonts: HashMap<FontFamilyName, LowercaseString>,
|
||||||
font_data: HashMap<FontIdentifier, Arc<Vec<u8>>>,
|
font_data: HashMap<FontIdentifier, Arc<Vec<u8>>>,
|
||||||
local_families: HashMap<LowercaseString, FontTemplates>,
|
local_families: FontStore,
|
||||||
web_families: HashMap<LowercaseString, FontTemplates>,
|
|
||||||
core_resource_thread: CoreResourceThread,
|
|
||||||
webrender_api: Box<dyn WebRenderFontApi>,
|
webrender_api: Box<dyn WebRenderFontApi>,
|
||||||
webrender_fonts: HashMap<FontIdentifier, FontKey>,
|
webrender_fonts: HashMap<FontIdentifier, FontKey>,
|
||||||
font_instances: HashMap<(FontKey, Au), FontInstanceKey>,
|
font_instances: HashMap<(FontKey, Au), FontInstanceKey>,
|
||||||
|
@ -198,9 +120,9 @@ impl FontCache {
|
||||||
let msg = self.port.recv().unwrap();
|
let msg = self.port.recv().unwrap();
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
Command::GetFontTemplates(descriptor_to_match, family_descriptor, result) => {
|
Command::GetFontTemplates(descriptor_to_match, font_family_name, result) => {
|
||||||
let templates =
|
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!("Found templates for descriptor {descriptor_to_match:?}: ");
|
||||||
debug!(" {templates:?}");
|
debug!(" {templates:?}");
|
||||||
|
|
||||||
|
@ -238,18 +160,25 @@ impl FontCache {
|
||||||
Command::GetFontInstance(identifier, pt_size, flags, result) => {
|
Command::GetFontInstance(identifier, pt_size, flags, result) => {
|
||||||
let _ = result.send(self.get_font_instance(identifier, pt_size, flags));
|
let _ = result.send(self.get_font_instance(identifier, pt_size, flags));
|
||||||
},
|
},
|
||||||
Command::AddWebFont(css_font_face_descriptors, sources, result) => {
|
Command::GetWebFont(bytes_receiver, font_index, result_sender) => {
|
||||||
self.handle_add_web_font(css_font_face_descriptors, sources, result);
|
self.webrender_api.forward_add_font_message(
|
||||||
|
bytes_receiver,
|
||||||
|
font_index,
|
||||||
|
result_sender,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
Command::AddDownloadedWebFont(css_font_face_descriptors, url, data, result) => {
|
Command::GetWebFontInstance(
|
||||||
let family_name = css_font_face_descriptors.family_name.clone();
|
font_key,
|
||||||
let templates = &mut self.web_families.get_mut(&family_name).unwrap();
|
font_size,
|
||||||
if let Ok(template) =
|
font_instance_flags,
|
||||||
FontTemplate::new_web_font(url, Arc::new(data), css_font_face_descriptors)
|
result_sender,
|
||||||
{
|
) => {
|
||||||
templates.add_template(template);
|
self.webrender_api.forward_add_font_instance_message(
|
||||||
}
|
font_key,
|
||||||
drop(result.send(()));
|
font_size,
|
||||||
|
font_instance_flags,
|
||||||
|
result_sender,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
Command::Ping => (),
|
Command::Ping => (),
|
||||||
Command::Exit(result) => {
|
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) {
|
fn refresh_local_families(&mut self) {
|
||||||
self.local_families.clear();
|
self.local_families.clear();
|
||||||
for_each_available_family(|family_name| {
|
for_each_available_family(|family_name| {
|
||||||
let family_name = LowercaseString::new(&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,
|
&mut self,
|
||||||
descriptor_to_match: &FontDescriptor,
|
descriptor_to_match: Option<&FontDescriptor>,
|
||||||
family_name: &FontFamilyName,
|
family_name: &FontFamilyName,
|
||||||
) -> Vec<FontTemplateRef> {
|
) -> Vec<FontTemplateRef> {
|
||||||
// TODO(Issue #188): look up localized font family names if canonical name not found
|
// 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
|
// if such family exists, try to match style to a font
|
||||||
let family_name = self.transform_family(family_name);
|
let family_name = self.transform_family(family_name);
|
||||||
self.local_families
|
self.local_families
|
||||||
|
.families
|
||||||
.get_mut(&family_name)
|
.get_mut(&family_name)
|
||||||
.map(|font_templates| {
|
.map(|font_templates| {
|
||||||
if font_templates.templates.is_empty() {
|
if font_templates.templates.is_empty() {
|
||||||
|
@ -414,41 +229,13 @@ impl FontCache {
|
||||||
.unwrap_or_default()
|
.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(
|
fn get_font_instance(
|
||||||
&mut self,
|
&mut self,
|
||||||
identifier: FontIdentifier,
|
identifier: FontIdentifier,
|
||||||
pt_size: Au,
|
pt_size: Au,
|
||||||
flags: FontInstanceFlags,
|
flags: FontInstanceFlags,
|
||||||
) -> FontInstanceKey {
|
) -> FontInstanceKey {
|
||||||
let webrender_api = &self.webrender_api;
|
let webrender_font_api = &self.webrender_api;
|
||||||
let webrender_fonts = &mut self.webrender_fonts;
|
let webrender_fonts = &mut self.webrender_fonts;
|
||||||
let font_data = self
|
let font_data = self
|
||||||
.font_data
|
.font_data
|
||||||
|
@ -466,22 +253,43 @@ impl FontCache {
|
||||||
// this for those platforms.
|
// this for those platforms.
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
if let FontIdentifier::Local(local_font_identifier) = identifier {
|
if let FontIdentifier::Local(local_font_identifier) = identifier {
|
||||||
return webrender_api
|
return webrender_font_api
|
||||||
.add_system_font(local_font_identifier.native_font_handle());
|
.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
|
*self
|
||||||
.font_instances
|
.font_instances
|
||||||
.entry((font_key, pt_size))
|
.entry((font_key, pt_size))
|
||||||
.or_insert_with(|| {
|
.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 public interface to the font cache thread, used by per-thread `FontContext` instances (via
|
||||||
/// the `FontSource` trait), and also by layout.
|
/// the `FontSource` trait), and also by layout.
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
@ -575,13 +383,9 @@ impl From<&FontFaceRuleData> for CSSFontFaceDescriptors {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontCacheThread {
|
impl FontCacheThread {
|
||||||
pub fn new(
|
pub fn new(webrender_api: Box<dyn WebRenderFontApi + Send>) -> FontCacheThread {
|
||||||
core_resource_thread: CoreResourceThread,
|
|
||||||
webrender_api: Box<dyn WebRenderFontApi + Send>,
|
|
||||||
) -> FontCacheThread {
|
|
||||||
let (chan, port) = ipc::channel().unwrap();
|
let (chan, port) = ipc::channel().unwrap();
|
||||||
|
|
||||||
let channel_to_self = chan.clone();
|
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("FontCache".to_owned())
|
.name("FontCache".to_owned())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
|
@ -591,12 +395,9 @@ impl FontCacheThread {
|
||||||
#[allow(clippy::default_constructed_unit_structs)]
|
#[allow(clippy::default_constructed_unit_structs)]
|
||||||
let mut cache = FontCache {
|
let mut cache = FontCache {
|
||||||
port,
|
port,
|
||||||
channel_to_self,
|
|
||||||
generic_fonts,
|
generic_fonts,
|
||||||
font_data: HashMap::new(),
|
font_data: HashMap::new(),
|
||||||
local_families: HashMap::new(),
|
local_families: Default::default(),
|
||||||
web_families: HashMap::new(),
|
|
||||||
core_resource_thread,
|
|
||||||
webrender_api,
|
webrender_api,
|
||||||
webrender_fonts: HashMap::new(),
|
webrender_fonts: HashMap::new(),
|
||||||
font_instances: HashMap::new(),
|
font_instances: HashMap::new(),
|
||||||
|
@ -610,55 +411,6 @@ impl FontCacheThread {
|
||||||
FontCacheThread { chan }
|
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) {
|
pub fn exit(&self) {
|
||||||
let (response_chan, response_port) = ipc::channel().unwrap();
|
let (response_chan, response_port) = ipc::channel().unwrap();
|
||||||
self.chan
|
self.chan
|
||||||
|
@ -671,8 +423,8 @@ impl FontCacheThread {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontSource for FontCacheThread {
|
impl FontSource for FontCacheThread {
|
||||||
fn get_font_instance(
|
fn get_system_font_instance(
|
||||||
&mut self,
|
&self,
|
||||||
identifier: FontIdentifier,
|
identifier: FontIdentifier,
|
||||||
size: Au,
|
size: Au,
|
||||||
flags: FontInstanceFlags,
|
flags: FontInstanceFlags,
|
||||||
|
@ -700,15 +452,15 @@ impl FontSource for FontCacheThread {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_matching_font_templates(
|
fn find_matching_font_templates(
|
||||||
&mut self,
|
&self,
|
||||||
descriptor_to_match: &FontDescriptor,
|
descriptor_to_match: Option<&FontDescriptor>,
|
||||||
family_descriptor: FontFamilyDescriptor,
|
font_family_name: &FontFamilyName,
|
||||||
) -> Vec<FontTemplateRef> {
|
) -> Vec<FontTemplateRef> {
|
||||||
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
|
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
|
||||||
self.chan
|
self.chan
|
||||||
.send(Command::GetFontTemplates(
|
.send(Command::GetFontTemplates(
|
||||||
descriptor_to_match.clone(),
|
descriptor_to_match.cloned(),
|
||||||
family_descriptor,
|
font_family_name.clone(),
|
||||||
response_chan,
|
response_chan,
|
||||||
))
|
))
|
||||||
.expect("failed to send message to font cache thread");
|
.expect("failed to send message to font cache thread");
|
||||||
|
@ -736,6 +488,35 @@ impl FontSource for FontCacheThread {
|
||||||
})
|
})
|
||||||
.collect()
|
.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)]
|
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
|
@ -771,35 +552,3 @@ impl fmt::Display for LowercaseString {
|
||||||
self.inner.fmt(f)
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,112 +9,72 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use fnv::FnvHasher;
|
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::{MallocSizeOf, MallocSizeOfOps};
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
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 servo_arc::Arc as ServoArc;
|
||||||
use style::computed_values::font_variant_caps::T as FontVariantCaps;
|
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 style::properties::style_structs::Font as FontStyleStruct;
|
||||||
use webrender_api::{FontInstanceFlags, FontInstanceKey};
|
use style::shared_lock::SharedRwLockReadGuard;
|
||||||
|
use style::stylesheets::{Stylesheet, StylesheetInDocument};
|
||||||
|
use style::Atom;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use crate::font::{Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef};
|
use crate::font::{
|
||||||
use crate::font_cache_thread::FontIdentifier;
|
Font, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontGroup, FontRef, FontSearchScope,
|
||||||
use crate::font_template::{FontTemplateRef, FontTemplateRefMethods};
|
};
|
||||||
|
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")]
|
#[cfg(target_os = "macos")]
|
||||||
use crate::platform::core_text_font_cache::CoreTextFontCache;
|
use crate::platform::core_text_font_cache::CoreTextFontCache;
|
||||||
|
|
||||||
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
|
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
|
/// The FontContext represents the per-thread/thread state necessary for
|
||||||
/// working with fonts. It is the public API used by the layout and
|
/// working with fonts. It is the public API used by the layout and
|
||||||
/// paint code. It talks directly to the font cache thread where
|
/// paint code. It talks directly to the font cache thread where
|
||||||
/// required.
|
/// required.
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FontContext<S: FontSource> {
|
pub struct FontContext<S: FontSource> {
|
||||||
font_source: Mutex<S>,
|
font_source: ReentrantMutex<S>,
|
||||||
|
resource_threads: ReentrantMutex<CoreResourceThread>,
|
||||||
// TODO: The font context holds a strong ref to the cached fonts
|
cache: CachingFontSource<S>,
|
||||||
// so they will never be released. Find out a good time to drop them.
|
web_fonts: CrossThreadFontStore,
|
||||||
// See bug https://github.com/servo/servo/issues/3300
|
webrender_font_store: CrossThreadWebRenderFontStore,
|
||||||
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>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: FontSource> MallocSizeOf for FontContext<S> {
|
impl<S: FontSource> MallocSizeOf for FontContext<S> {
|
||||||
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||||
let font_cache_size = self
|
self.cache.size_of(ops)
|
||||||
.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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: FontSource> FontContext<S> {
|
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)]
|
#[allow(clippy::default_constructed_unit_structs)]
|
||||||
FontContext {
|
FontContext {
|
||||||
font_source: Mutex::new(font_source),
|
font_source: ReentrantMutex::new(font_source.clone()),
|
||||||
font_cache: RwLock::default(),
|
resource_threads: ReentrantMutex::new(resource_threads.core_thread),
|
||||||
font_template_cache: RwLock::default(),
|
cache: CachingFontSource::new(font_source),
|
||||||
font_group_cache: RwLock::default(),
|
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
|
/// Invalidate all caches that this [`FontContext`] holds and any in-process platform-specific
|
||||||
/// caches.
|
/// 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) {
|
pub fn invalidate_caches(&self) {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
CoreTextFontCache::clear_core_text_font_cache();
|
CoreTextFontCache::clear_core_text_font_cache();
|
||||||
|
self.cache.invalidate()
|
||||||
self.font_cache.write().clear();
|
|
||||||
self.font_template_cache.write().clear();
|
|
||||||
self.font_group_cache.write().clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`.
|
/// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`.
|
||||||
|
@ -132,17 +92,7 @@ impl<S: FontSource> FontContext<S> {
|
||||||
style: ServoArc<FontStyleStruct>,
|
style: ServoArc<FontStyleStruct>,
|
||||||
size: Au,
|
size: Au,
|
||||||
) -> Arc<RwLock<FontGroup>> {
|
) -> Arc<RwLock<FontGroup>> {
|
||||||
let cache_key = FontGroupCacheKey { size, style };
|
self.cache.font_group_with_size(style, size)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a font matching the parameters. Fonts are cached, so repeated calls will return a
|
/// 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(),
|
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;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +153,7 @@ impl<S: FontSource> FontContext<S> {
|
||||||
synthesized_small_caps_font,
|
synthesized_small_caps_font,
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
self.font_cache.write().insert(cache_key, font.clone());
|
self.cache.fonts.write().insert(cache_key, font.clone());
|
||||||
font
|
font
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,30 +162,23 @@ impl<S: FontSource> FontContext<S> {
|
||||||
descriptor_to_match: &FontDescriptor,
|
descriptor_to_match: &FontDescriptor,
|
||||||
family_descriptor: &FontFamilyDescriptor,
|
family_descriptor: &FontFamilyDescriptor,
|
||||||
) -> Vec<FontTemplateRef> {
|
) -> Vec<FontTemplateRef> {
|
||||||
let cache_key = FontTemplateCacheKey {
|
// First try to find an appropriate web font that matches this descriptor.
|
||||||
font_descriptor: descriptor_to_match.clone(),
|
if family_descriptor.scope == FontSearchScope::Any {
|
||||||
family_descriptor: family_descriptor.clone(),
|
let family_name = LowercaseString::from(&family_descriptor.name);
|
||||||
};
|
if let Some(templates) = self
|
||||||
|
.web_fonts
|
||||||
if let Some(templates) = self.font_template_cache.read().get(&cache_key).cloned() {
|
.read()
|
||||||
return templates;
|
.families
|
||||||
|
.get(&family_name)
|
||||||
|
.map(|templates| templates.find_for_descriptor(Some(descriptor_to_match)))
|
||||||
|
{
|
||||||
|
return templates;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(
|
// If not request a matching font from the system font cache.
|
||||||
"FontContext::font_template cache miss for template_descriptor={:?} family_descriptor={:?}",
|
self.cache
|
||||||
descriptor_to_match,
|
.matching_templates(descriptor_to_match, family_descriptor)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a `Font` for use in layout calculations, from a `FontTemplateData` returned by the
|
/// 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(),
|
font_descriptor.clone(),
|
||||||
synthesized_small_caps,
|
synthesized_small_caps,
|
||||||
)?;
|
)?;
|
||||||
font.font_key = self.font_source.lock().get_font_instance(
|
|
||||||
font_template.identifier(),
|
let font_source = self.font_source.lock();
|
||||||
font_descriptor.pt_size,
|
font.font_key = match font_template.identifier() {
|
||||||
font.webrender_font_instance_flags(),
|
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))
|
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)]
|
#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
|
||||||
struct FontCacheKey {
|
struct FontCacheKey {
|
||||||
font_identifier: FontIdentifier,
|
font_identifier: FontIdentifier,
|
||||||
|
@ -296,3 +636,35 @@ impl Hash for FontGroupCacheKey {
|
||||||
self.style.hash.hash(hasher)
|
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
|
||||||
|
}
|
||||||
|
|
130
components/gfx/font_store.rs
Normal file
130
components/gfx/font_store.rs
Normal file
|
@ -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)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -100,7 +100,7 @@ impl FontTemplateDescriptor {
|
||||||
|
|
||||||
fn override_values_with_css_font_template_descriptors(
|
fn override_values_with_css_font_template_descriptors(
|
||||||
&mut self,
|
&mut self,
|
||||||
css_font_template_descriptors: CSSFontFaceDescriptors,
|
css_font_template_descriptors: &CSSFontFaceDescriptors,
|
||||||
) {
|
) {
|
||||||
if let Some(weight) = css_font_template_descriptors.weight {
|
if let Some(weight) = css_font_template_descriptors.weight {
|
||||||
self.weight = weight;
|
self.weight = weight;
|
||||||
|
@ -117,8 +117,8 @@ impl FontTemplateDescriptor {
|
||||||
if let Some(stretch) = css_font_template_descriptors.stretch {
|
if let Some(stretch) = css_font_template_descriptors.stretch {
|
||||||
self.stretch = stretch;
|
self.stretch = stretch;
|
||||||
}
|
}
|
||||||
if let Some(unicode_range) = css_font_template_descriptors.unicode_range {
|
if let Some(ref unicode_range) = css_font_template_descriptors.unicode_range {
|
||||||
self.unicode_range = Some(unicode_range);
|
self.unicode_range = Some(unicode_range.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,7 @@ impl FontTemplateDescriptor {
|
||||||
/// This describes all the information needed to create
|
/// This describes all the information needed to create
|
||||||
/// font instance handles. It contains a unique
|
/// font instance handles. It contains a unique
|
||||||
/// FontTemplateData structure that is platform specific.
|
/// FontTemplateData structure that is platform specific.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct FontTemplate {
|
pub struct FontTemplate {
|
||||||
pub identifier: FontIdentifier,
|
pub identifier: FontIdentifier,
|
||||||
pub descriptor: FontTemplateDescriptor,
|
pub descriptor: FontTemplateDescriptor,
|
||||||
|
@ -154,7 +155,8 @@ impl Debug for FontTemplate {
|
||||||
/// is common, regardless of the number of instances of
|
/// is common, regardless of the number of instances of
|
||||||
/// this font handle per thread.
|
/// this font handle per thread.
|
||||||
impl FontTemplate {
|
impl FontTemplate {
|
||||||
pub fn new_local(
|
/// Create a new [`FontTemplate`] for a system font installed locally.
|
||||||
|
pub fn new_for_local_font(
|
||||||
identifier: LocalFontIdentifier,
|
identifier: LocalFontIdentifier,
|
||||||
descriptor: FontTemplateDescriptor,
|
descriptor: FontTemplateDescriptor,
|
||||||
) -> FontTemplate {
|
) -> 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,
|
url: ServoUrl,
|
||||||
data: Arc<Vec<u8>>,
|
data: Arc<Vec<u8>>,
|
||||||
css_font_template_descriptors: CSSFontFaceDescriptors,
|
css_font_template_descriptors: &CSSFontFaceDescriptors,
|
||||||
) -> Result<FontTemplate, &'static str> {
|
) -> Result<FontTemplate, &'static str> {
|
||||||
let identifier = FontIdentifier::Web(url.clone());
|
let identifier = FontIdentifier::Web(url.clone());
|
||||||
let Ok(handle) = PlatformFont::new_from_data(identifier, data.clone(), 0, None) else {
|
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 {
|
pub fn identifier(&self) -> &FontIdentifier {
|
||||||
&self.identifier
|
&self.identifier
|
||||||
}
|
}
|
||||||
|
@ -233,6 +250,10 @@ impl FontTemplateRefMethods for FontTemplateRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn data(&self) -> Arc<Vec<u8>> {
|
fn data(&self) -> Arc<Vec<u8>> {
|
||||||
|
if let Some(data) = self.borrow().data.clone() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
let mut template = self.borrow_mut();
|
let mut template = self.borrow_mut();
|
||||||
let identifier = template.identifier.clone();
|
let identifier = template.identifier.clone();
|
||||||
template
|
template
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
pub mod font;
|
pub mod font;
|
||||||
pub mod font_cache_thread;
|
pub mod font_cache_thread;
|
||||||
pub mod font_context;
|
pub mod font_context;
|
||||||
|
pub mod font_store;
|
||||||
pub mod font_template;
|
pub mod font_template;
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
pub mod platform;
|
pub mod platform;
|
||||||
|
|
|
@ -486,7 +486,10 @@ where
|
||||||
None => StyleFontStyle::NORMAL,
|
None => StyleFontStyle::NORMAL,
|
||||||
};
|
};
|
||||||
let descriptor = FontTemplateDescriptor::new(weight, stretch, style);
|
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) {
|
if let Some(family) = FONT_LIST.find_family(family_name) {
|
||||||
|
|
|
@ -152,7 +152,10 @@ where
|
||||||
};
|
};
|
||||||
let descriptor = FontTemplateDescriptor::new(weight, stretch, style);
|
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);
|
FcFontSetDestroy(matches);
|
||||||
|
|
|
@ -158,7 +158,10 @@ where
|
||||||
stretch,
|
stretch,
|
||||||
style,
|
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) {
|
if let Some(family) = FONT_LIST.find_family(family_name) {
|
||||||
|
|
|
@ -81,7 +81,7 @@ where
|
||||||
postscript_name: Atom::from(family_descriptor.font_name()),
|
postscript_name: Atom::from(family_descriptor.font_name()),
|
||||||
path: Atom::from(path),
|
path: Atom::from(path),
|
||||||
};
|
};
|
||||||
callback(FontTemplate::new_local(identifier, descriptor));
|
callback(FontTemplate::new_for_local_font(identifier, descriptor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ where
|
||||||
let local_font_identifier = LocalFontIdentifier {
|
let local_font_identifier = LocalFontIdentifier {
|
||||||
font_descriptor: Arc::new(font.to_descriptor()),
|
font_descriptor: Arc::new(font.to_descriptor()),
|
||||||
};
|
};
|
||||||
callback(FontTemplate::new_local(
|
callback(FontTemplate::new_for_local_font(
|
||||||
local_font_identifier,
|
local_font_identifier,
|
||||||
template_descriptor,
|
template_descriptor,
|
||||||
))
|
))
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
@ -13,9 +13,12 @@ use app_units::Au;
|
||||||
use gfx::font::{
|
use gfx::font::{
|
||||||
fallback_font_families, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope,
|
fallback_font_families, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope,
|
||||||
};
|
};
|
||||||
use gfx::font_cache_thread::{CSSFontFaceDescriptors, FontIdentifier, FontTemplates};
|
use gfx::font_cache_thread::{CSSFontFaceDescriptors, FontIdentifier, FontSource};
|
||||||
use gfx::font_context::{FontContext, FontSource};
|
use gfx::font_context::FontContext;
|
||||||
|
use gfx::font_store::FontTemplates;
|
||||||
use gfx::font_template::{FontTemplate, FontTemplateRef};
|
use gfx::font_template::{FontTemplate, FontTemplateRef};
|
||||||
|
use ipc_channel::ipc;
|
||||||
|
use net_traits::ResourceThreads;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use servo_atoms::Atom;
|
use servo_atoms::Atom;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
@ -27,15 +30,16 @@ use style::values::computed::font::{
|
||||||
};
|
};
|
||||||
use style::values::computed::{FontLanguageOverride, XLang};
|
use style::values::computed::{FontLanguageOverride, XLang};
|
||||||
use style::values::generics::font::LineHeight;
|
use style::values::generics::font::LineHeight;
|
||||||
use webrender_api::{FontInstanceFlags, FontInstanceKey, IdNamespace};
|
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, IdNamespace};
|
||||||
|
|
||||||
struct TestFontSource {
|
#[derive(Clone)]
|
||||||
families: HashMap<String, FontTemplates>,
|
struct MockFontCacheThread {
|
||||||
|
families: RefCell<HashMap<String, FontTemplates>>,
|
||||||
find_font_count: Rc<Cell<isize>>,
|
find_font_count: Rc<Cell<isize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestFontSource {
|
impl MockFontCacheThread {
|
||||||
fn new() -> TestFontSource {
|
fn new() -> MockFontCacheThread {
|
||||||
let mut csstest_ascii = FontTemplates::default();
|
let mut csstest_ascii = FontTemplates::default();
|
||||||
Self::add_face(&mut csstest_ascii, "csstest-ascii");
|
Self::add_face(&mut csstest_ascii, "csstest-ascii");
|
||||||
|
|
||||||
|
@ -50,8 +54,8 @@ impl TestFontSource {
|
||||||
families.insert("CSSTest Basic".to_owned(), csstest_basic);
|
families.insert("CSSTest Basic".to_owned(), csstest_basic);
|
||||||
families.insert(fallback_font_families(None)[0].to_owned(), fallback);
|
families.insert(fallback_font_families(None)[0].to_owned(), fallback);
|
||||||
|
|
||||||
TestFontSource {
|
MockFontCacheThread {
|
||||||
families,
|
families: RefCell::new(families),
|
||||||
find_font_count: Rc::new(Cell::new(0)),
|
find_font_count: Rc::new(Cell::new(0)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,36 +81,50 @@ impl TestFontSource {
|
||||||
let file = File::open(path).unwrap();
|
let file = File::open(path).unwrap();
|
||||||
let data: Vec<u8> = file.bytes().map(|b| b.unwrap()).collect();
|
let data: Vec<u8> = file.bytes().map(|b| b.unwrap()).collect();
|
||||||
family.add_template(
|
family.add_template(
|
||||||
FontTemplate::new_web_font(
|
FontTemplate::new_for_remote_web_font(
|
||||||
Self::url_for_font_name(name),
|
Self::url_for_font_name(name),
|
||||||
std::sync::Arc::new(data),
|
std::sync::Arc::new(data),
|
||||||
CSSFontFaceDescriptors::new(name),
|
&CSSFontFaceDescriptors::new(name),
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontSource for TestFontSource {
|
impl FontSource for MockFontCacheThread {
|
||||||
fn get_font_instance(
|
fn find_matching_font_templates(
|
||||||
&mut self,
|
&self,
|
||||||
_font_identifier: FontIdentifier,
|
descriptor_to_match: Option<&FontDescriptor>,
|
||||||
_size: Au,
|
font_family_name: &FontFamilyName,
|
||||||
_flags: FontInstanceFlags,
|
) -> Vec<FontTemplateRef> {
|
||||||
|
self.find_font_count.set(self.find_font_count.get() + 1);
|
||||||
|
self.families
|
||||||
|
.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 {
|
||||||
FontInstanceKey(IdNamespace(0), 0)
|
FontInstanceKey(IdNamespace(0), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_matching_font_templates(
|
fn get_web_font(&self, _: std::sync::Arc<Vec<u8>>, _: u32) -> webrender_api::FontKey {
|
||||||
&mut self,
|
FontKey(IdNamespace(0), 0)
|
||||||
descriptor_to_match: &FontDescriptor,
|
}
|
||||||
family_descriptor: FontFamilyDescriptor,
|
|
||||||
) -> Vec<FontTemplateRef> {
|
fn get_web_font_instance(
|
||||||
self.find_font_count.set(self.find_font_count.get() + 1);
|
&self,
|
||||||
self.families
|
_: webrender_api::FontKey,
|
||||||
.get_mut(family_descriptor.name())
|
_: f32,
|
||||||
.map(|family| family.find_for_descriptor(descriptor_to_match))
|
_: FontInstanceFlags,
|
||||||
.unwrap_or_default()
|
) -> FontInstanceKey {
|
||||||
|
FontInstanceKey(IdNamespace(0), 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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]
|
#[test]
|
||||||
fn test_font_group_is_cached_by_style() {
|
fn test_font_group_is_cached_by_style() {
|
||||||
let source = TestFontSource::new();
|
let source = MockFontCacheThread::new();
|
||||||
let context = FontContext::new(source);
|
let context = FontContext::new(source, mock_resource_threads());
|
||||||
|
|
||||||
let style1 = style();
|
let style1 = style();
|
||||||
|
|
||||||
|
@ -176,9 +200,9 @@ fn test_font_group_is_cached_by_style() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_font_group_find_by_codepoint() {
|
fn test_font_group_find_by_codepoint() {
|
||||||
let source = TestFontSource::new();
|
let source = MockFontCacheThread::new();
|
||||||
let count = source.find_font_count.clone();
|
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();
|
let mut style = style();
|
||||||
style.set_font_family(font_family(vec!["CSSTest ASCII", "CSSTest Basic"]));
|
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();
|
let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
font.identifier(),
|
font.identifier(),
|
||||||
TestFontSource::identifier_for_font_name("csstest-ascii")
|
MockFontCacheThread::identifier_for_font_name("csstest-ascii")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
count.get(),
|
count.get(),
|
||||||
|
@ -199,7 +223,7 @@ fn test_font_group_find_by_codepoint() {
|
||||||
let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
|
let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
font.identifier(),
|
font.identifier(),
|
||||||
TestFontSource::identifier_for_font_name("csstest-ascii")
|
MockFontCacheThread::identifier_for_font_name("csstest-ascii")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
count.get(),
|
count.get(),
|
||||||
|
@ -210,15 +234,15 @@ fn test_font_group_find_by_codepoint() {
|
||||||
let font = group.write().find_by_codepoint(&mut context, 'á').unwrap();
|
let font = group.write().find_by_codepoint(&mut context, 'á').unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
font.identifier(),
|
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");
|
assert_eq!(count.get(), 2, "both fonts should now have been loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_font_fallback() {
|
fn test_font_fallback() {
|
||||||
let source = TestFontSource::new();
|
let source = MockFontCacheThread::new();
|
||||||
let mut context = FontContext::new(source);
|
let mut context = FontContext::new(source, mock_resource_threads());
|
||||||
|
|
||||||
let mut style = style();
|
let mut style = style();
|
||||||
style.set_font_family(font_family(vec!["CSSTest ASCII"]));
|
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();
|
let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
font.identifier(),
|
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"
|
"a family in the group should be used if there is a matching glyph"
|
||||||
);
|
);
|
||||||
|
|
||||||
let font = group.write().find_by_codepoint(&mut context, 'á').unwrap();
|
let font = group.write().find_by_codepoint(&mut context, 'á').unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
font.identifier(),
|
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"
|
"a fallback font should be used if there is no matching glyph in the group"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_font_template_is_cached() {
|
fn test_font_template_is_cached() {
|
||||||
let source = TestFontSource::new();
|
let source = MockFontCacheThread::new();
|
||||||
let count = source.find_font_count.clone();
|
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 {
|
let mut font_descriptor = FontDescriptor {
|
||||||
weight: FontWeight::normal(),
|
weight: FontWeight::normal(),
|
||||||
|
|
|
@ -26,7 +26,7 @@ use fnv::FnvHashMap;
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use gfx::font;
|
use gfx::font;
|
||||||
use gfx::font_cache_thread::FontCacheThread;
|
use gfx::font_cache_thread::FontCacheThread;
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::{FontContext, FontContextWebFontMethods};
|
||||||
use histogram::Histogram;
|
use histogram::Histogram;
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
|
@ -54,6 +54,7 @@ use log::{debug, error, trace, warn};
|
||||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||||
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
|
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
|
||||||
use net_traits::image_cache::{ImageCache, UsePlaceholder};
|
use net_traits::image_cache::{ImageCache, UsePlaceholder};
|
||||||
|
use net_traits::ResourceThreads;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use profile_traits::mem::{Report, ReportKind};
|
use profile_traits::mem::{Report, ReportKind};
|
||||||
use profile_traits::path;
|
use profile_traits::path;
|
||||||
|
@ -133,13 +134,10 @@ pub struct LayoutThread {
|
||||||
/// Reference to the script thread image cache.
|
/// Reference to the script thread image cache.
|
||||||
image_cache: Arc<dyn ImageCache>,
|
image_cache: Arc<dyn ImageCache>,
|
||||||
|
|
||||||
/// Public interface to the font cache thread.
|
/// A FontContext tFontCacheThreadImplg layout.
|
||||||
font_cache_thread: FontCacheThread,
|
|
||||||
|
|
||||||
/// A FontContext to be used during layout.
|
|
||||||
font_context: Arc<FontContext<FontCacheThread>>,
|
font_context: Arc<FontContext<FontCacheThread>>,
|
||||||
|
|
||||||
/// Is this the first reflow in this LayoutThread?
|
/// Is this the first reflow iFontCacheThreadImplread?
|
||||||
first_reflow: Cell<bool>,
|
first_reflow: Cell<bool>,
|
||||||
|
|
||||||
/// Flag to indicate whether to use parallel operations
|
/// Flag to indicate whether to use parallel operations
|
||||||
|
@ -207,6 +205,7 @@ impl LayoutFactory for LayoutFactoryImpl {
|
||||||
config.constellation_chan,
|
config.constellation_chan,
|
||||||
config.script_chan,
|
config.script_chan,
|
||||||
config.image_cache,
|
config.image_cache,
|
||||||
|
config.resource_threads,
|
||||||
config.font_cache_thread,
|
config.font_cache_thread,
|
||||||
config.time_profiler_chan,
|
config.time_profiler_chan,
|
||||||
config.webrender_api_sender,
|
config.webrender_api_sender,
|
||||||
|
@ -566,6 +565,7 @@ impl LayoutThread {
|
||||||
constellation_chan: IpcSender<ConstellationMsg>,
|
constellation_chan: IpcSender<ConstellationMsg>,
|
||||||
script_chan: IpcSender<ConstellationControlMsg>,
|
script_chan: IpcSender<ConstellationControlMsg>,
|
||||||
image_cache: Arc<dyn ImageCache>,
|
image_cache: Arc<dyn ImageCache>,
|
||||||
|
resource_threads: ResourceThreads,
|
||||||
font_cache_thread: FontCacheThread,
|
font_cache_thread: FontCacheThread,
|
||||||
time_profiler_chan: profile_time::ProfilerChan,
|
time_profiler_chan: profile_time::ProfilerChan,
|
||||||
webrender_api: WebRenderScriptApi,
|
webrender_api: WebRenderScriptApi,
|
||||||
|
@ -575,7 +575,7 @@ impl LayoutThread {
|
||||||
// Let webrender know about this pipeline by sending an empty display list.
|
// Let webrender know about this pipeline by sending an empty display list.
|
||||||
webrender_api.send_initial_transaction(id.into());
|
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(
|
let device = Device::new(
|
||||||
MediaType::screen(),
|
MediaType::screen(),
|
||||||
QuirksMode::NoQuirks,
|
QuirksMode::NoQuirks,
|
||||||
|
@ -604,7 +604,6 @@ impl LayoutThread {
|
||||||
time_profiler_chan,
|
time_profiler_chan,
|
||||||
registered_painters: RegisteredPaintersImpl(Default::default()),
|
registered_painters: RegisteredPaintersImpl(Default::default()),
|
||||||
image_cache,
|
image_cache,
|
||||||
font_cache_thread,
|
|
||||||
font_context,
|
font_context,
|
||||||
first_reflow: Cell::new(true),
|
first_reflow: Cell::new(true),
|
||||||
font_cache_sender: ipc_font_cache_sender,
|
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.
|
// Find all font-face rules and notify the font cache of them.
|
||||||
// GWTODO: Need to handle unloading web fonts.
|
// GWTODO: Need to handle unloading web fonts.
|
||||||
if stylesheet.is_effective_for_device(self.stylist.device(), guard) {
|
if stylesheet.is_effective_for_device(self.stylist.device(), guard) {
|
||||||
let newly_loading_font_count =
|
let newly_loading_font_count = self.font_context.add_all_web_fonts_from_stylesheet(
|
||||||
self.font_cache_thread.add_all_web_fonts_from_stylesheet(
|
stylesheet,
|
||||||
stylesheet,
|
guard,
|
||||||
guard,
|
self.stylist.device(),
|
||||||
self.stylist.device(),
|
&self.font_cache_sender,
|
||||||
&self.font_cache_sender,
|
self.debug.load_webfonts_synchronously,
|
||||||
self.debug.load_webfonts_synchronously,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if !self.debug.load_webfonts_synchronously {
|
if !self.debug.load_webfonts_synchronously {
|
||||||
self.outstanding_web_fonts
|
self.outstanding_web_fonts
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
@ -24,7 +25,7 @@ use euclid::{Point2D, Scale, Size2D, Vector2D};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
||||||
use gfx::font_cache_thread::FontCacheThread;
|
use gfx::font_cache_thread::FontCacheThread;
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::{FontContext, FontContextWebFontMethods};
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
|
@ -41,6 +42,7 @@ use log::{debug, error, warn};
|
||||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||||
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
|
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
|
||||||
use net_traits::image_cache::{ImageCache, UsePlaceholder};
|
use net_traits::image_cache::{ImageCache, UsePlaceholder};
|
||||||
|
use net_traits::ResourceThreads;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use profile_traits::mem::{Report, ReportKind};
|
use profile_traits::mem::{Report, ReportKind};
|
||||||
use profile_traits::path;
|
use profile_traits::path;
|
||||||
|
@ -120,10 +122,6 @@ pub struct LayoutThread {
|
||||||
/// Reference to the script thread image cache.
|
/// Reference to the script thread image cache.
|
||||||
image_cache: Arc<dyn ImageCache>,
|
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.
|
/// A FontContext to be used during layout.
|
||||||
font_context: Arc<FontContext<FontCacheThread>>,
|
font_context: Arc<FontContext<FontCacheThread>>,
|
||||||
|
|
||||||
|
@ -183,6 +181,7 @@ impl LayoutFactory for LayoutFactoryImpl {
|
||||||
config.constellation_chan,
|
config.constellation_chan,
|
||||||
config.script_chan,
|
config.script_chan,
|
||||||
config.image_cache,
|
config.image_cache,
|
||||||
|
config.resource_threads,
|
||||||
config.font_cache_thread,
|
config.font_cache_thread,
|
||||||
config.time_profiler_chan,
|
config.time_profiler_chan,
|
||||||
config.webrender_api_sender,
|
config.webrender_api_sender,
|
||||||
|
@ -483,6 +482,7 @@ impl LayoutThread {
|
||||||
constellation_chan: IpcSender<ConstellationMsg>,
|
constellation_chan: IpcSender<ConstellationMsg>,
|
||||||
script_chan: IpcSender<ConstellationControlMsg>,
|
script_chan: IpcSender<ConstellationControlMsg>,
|
||||||
image_cache: Arc<dyn ImageCache>,
|
image_cache: Arc<dyn ImageCache>,
|
||||||
|
resource_threads: ResourceThreads,
|
||||||
font_cache_thread: FontCacheThread,
|
font_cache_thread: FontCacheThread,
|
||||||
time_profiler_chan: profile_time::ProfilerChan,
|
time_profiler_chan: profile_time::ProfilerChan,
|
||||||
webrender_api_sender: WebRenderScriptApi,
|
webrender_api_sender: WebRenderScriptApi,
|
||||||
|
@ -494,7 +494,7 @@ impl LayoutThread {
|
||||||
|
|
||||||
// The device pixel ratio is incorrect (it does not have the hidpi value),
|
// 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.
|
// 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(
|
let device = Device::new(
|
||||||
MediaType::screen(),
|
MediaType::screen(),
|
||||||
QuirksMode::NoQuirks,
|
QuirksMode::NoQuirks,
|
||||||
|
@ -523,7 +523,6 @@ impl LayoutThread {
|
||||||
time_profiler_chan,
|
time_profiler_chan,
|
||||||
registered_painters: RegisteredPaintersImpl(Default::default()),
|
registered_painters: RegisteredPaintersImpl(Default::default()),
|
||||||
image_cache,
|
image_cache,
|
||||||
font_cache_thread,
|
|
||||||
font_context,
|
font_context,
|
||||||
first_reflow: Cell::new(true),
|
first_reflow: Cell::new(true),
|
||||||
font_cache_sender: ipc_font_cache_sender,
|
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.
|
// Find all font-face rules and notify the font cache of them.
|
||||||
// GWTODO: Need to handle unloading web fonts.
|
// GWTODO: Need to handle unloading web fonts.
|
||||||
if stylesheet.is_effective_for_device(self.stylist.device(), guard) {
|
if stylesheet.is_effective_for_device(self.stylist.device(), guard) {
|
||||||
let newly_loading_font_count =
|
let newly_loading_font_count = self.font_context.add_all_web_fonts_from_stylesheet(
|
||||||
self.font_cache_thread.add_all_web_fonts_from_stylesheet(
|
stylesheet,
|
||||||
stylesheet,
|
guard,
|
||||||
guard,
|
self.stylist.device(),
|
||||||
self.stylist.device(),
|
&self.font_cache_sender,
|
||||||
&self.font_cache_sender,
|
self.debug.load_webfonts_synchronously,
|
||||||
self.debug.load_webfonts_synchronously,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if !self.debug.load_webfonts_synchronously {
|
if !self.debug.load_webfonts_synchronously {
|
||||||
self.outstanding_web_fonts
|
self.outstanding_web_fonts
|
||||||
|
@ -1243,7 +1241,6 @@ impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct LayoutFontMetricsProvider(Arc<FontContext<FontCacheThread>>);
|
struct LayoutFontMetricsProvider(Arc<FontContext<FontCacheThread>>);
|
||||||
|
|
||||||
impl FontMetricsProvider for LayoutFontMetricsProvider {
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3536,6 +3536,7 @@ impl ScriptThread {
|
||||||
script_chan: self.control_chan.clone(),
|
script_chan: self.control_chan.clone(),
|
||||||
image_cache: self.image_cache.clone(),
|
image_cache: self.image_cache.clone(),
|
||||||
font_cache_thread: self.font_cache_thread.clone(),
|
font_cache_thread: self.font_cache_thread.clone(),
|
||||||
|
resource_threads: self.resource_threads.clone(),
|
||||||
time_profiler_chan: self.time_profiler_chan.clone(),
|
time_profiler_chan: self.time_profiler_chan.clone(),
|
||||||
webrender_api_sender: self.webrender_api_sender.clone(),
|
webrender_api_sender: self.webrender_api_sender.clone(),
|
||||||
paint_time_metrics,
|
paint_time_metrics,
|
||||||
|
|
|
@ -72,7 +72,6 @@ use ipc_channel::ipc::{self, IpcSender};
|
||||||
use log::{error, trace, warn, Log, Metadata, Record};
|
use log::{error, trace, warn, Log, Metadata, Record};
|
||||||
use media::{GLPlayerThreads, GlApi, NativeDisplay, WindowGLContext};
|
use media::{GLPlayerThreads, GlApi, NativeDisplay, WindowGLContext};
|
||||||
use net::resource_thread::new_resource_threads;
|
use net::resource_thread::new_resource_threads;
|
||||||
use net_traits::IpcSend;
|
|
||||||
use profile::{mem as profile_mem, time as profile_time};
|
use profile::{mem as profile_mem, time as profile_time};
|
||||||
use profile_traits::{mem, time};
|
use profile_traits::{mem, time};
|
||||||
use script::serviceworker_manager::ServiceWorkerManager;
|
use script::serviceworker_manager::ServiceWorkerManager;
|
||||||
|
@ -996,14 +995,14 @@ fn create_constellation(
|
||||||
opts.ignore_certificate_errors,
|
opts.ignore_certificate_errors,
|
||||||
);
|
);
|
||||||
|
|
||||||
let font_cache_thread = FontCacheThread::new(
|
let font_cache_thread = FontCacheThread::new(Box::new(WebRenderFontApiCompositorProxy(
|
||||||
public_resource_threads.sender(),
|
compositor_proxy.clone(),
|
||||||
Box::new(FontCacheWR(compositor_proxy.clone())),
|
)));
|
||||||
);
|
|
||||||
|
|
||||||
let (canvas_create_sender, canvas_ipc_sender) = CanvasPaintThread::start(
|
let (canvas_create_sender, canvas_ipc_sender) = CanvasPaintThread::start(
|
||||||
Box::new(CanvasWebrenderApi(compositor_proxy.clone())),
|
Box::new(CanvasWebrenderApi(compositor_proxy.clone())),
|
||||||
font_cache_thread.clone(),
|
font_cache_thread.clone(),
|
||||||
|
public_resource_threads.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let initial_state = InitialConstellationState {
|
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(
|
fn add_font_instance(
|
||||||
&self,
|
&self,
|
||||||
font_key: FontKey,
|
font_key: FontKey,
|
||||||
|
@ -1065,6 +1064,7 @@ impl WebRenderFontApi for FontCacheWR {
|
||||||
)));
|
)));
|
||||||
receiver.recv().unwrap()
|
receiver.recv().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey {
|
fn add_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey {
|
||||||
let (sender, receiver) = unbounded();
|
let (sender, receiver) = unbounded();
|
||||||
let (bytes_sender, bytes_receiver) =
|
let (bytes_sender, bytes_receiver) =
|
||||||
|
@ -1085,6 +1085,35 @@ impl WebRenderFontApi for FontCacheWR {
|
||||||
)));
|
)));
|
||||||
receiver.recv().unwrap()
|
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)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -11,6 +11,7 @@ name = "gfx_traits"
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
ipc-channel = { workspace = true }
|
||||||
malloc_size_of = { workspace = true }
|
malloc_size_of = { workspace = true }
|
||||||
malloc_size_of_derive = { workspace = true }
|
malloc_size_of_derive = { workspace = true }
|
||||||
range = { path = "../../range" }
|
range = { path = "../../range" }
|
||||||
|
|
|
@ -341,7 +341,7 @@ where
|
||||||
// See also: https://github.com/servo/servo/blob/735480/components/script/script_thread.rs#L313
|
// See also: https://github.com/servo/servo/blob/735480/components/script/script_thread.rs#L313
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct ResourceThreads {
|
pub struct ResourceThreads {
|
||||||
core_thread: CoreResourceThread,
|
pub core_thread: CoreResourceThread,
|
||||||
storage_thread: IpcSender<StorageThreadMsg>,
|
storage_thread: IpcSender<StorageThreadMsg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ use libc::c_void;
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
use malloc_size_of_derive::MallocSizeOf;
|
||||||
use metrics::PaintTimeMetrics;
|
use metrics::PaintTimeMetrics;
|
||||||
use net_traits::image_cache::{ImageCache, PendingImageId};
|
use net_traits::image_cache::{ImageCache, PendingImageId};
|
||||||
|
use net_traits::ResourceThreads;
|
||||||
use profile_traits::mem::Report;
|
use profile_traits::mem::Report;
|
||||||
use profile_traits::time;
|
use profile_traits::time;
|
||||||
use script_traits::{
|
use script_traits::{
|
||||||
|
@ -164,6 +165,7 @@ pub struct LayoutConfig {
|
||||||
pub constellation_chan: IpcSender<LayoutMsg>,
|
pub constellation_chan: IpcSender<LayoutMsg>,
|
||||||
pub script_chan: IpcSender<ConstellationControlMsg>,
|
pub script_chan: IpcSender<ConstellationControlMsg>,
|
||||||
pub image_cache: Arc<dyn ImageCache>,
|
pub image_cache: Arc<dyn ImageCache>,
|
||||||
|
pub resource_threads: ResourceThreads,
|
||||||
pub font_cache_thread: FontCacheThread,
|
pub font_cache_thread: FontCacheThread,
|
||||||
pub time_profiler_chan: time::ProfilerChan,
|
pub time_profiler_chan: time::ProfilerChan,
|
||||||
pub webrender_api_sender: WebRenderScriptApi,
|
pub webrender_api_sender: WebRenderScriptApi,
|
||||||
|
|
|
@ -188,6 +188,23 @@ pub trait WebRenderFontApi {
|
||||||
flags: FontInstanceFlags,
|
flags: FontInstanceFlags,
|
||||||
) -> FontInstanceKey;
|
) -> FontInstanceKey;
|
||||||
fn add_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey;
|
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;
|
fn add_system_font(&self, handle: NativeFontHandle) -> FontKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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": [
|
"first-available-font-001.html": [
|
||||||
"5eb88f7bf6713b80e0adb4728681e62c3f2dc2bc",
|
"5eb88f7bf6713b80e0adb4728681e62c3f2dc2bc",
|
||||||
[
|
[
|
||||||
|
@ -396276,6 +396289,10 @@
|
||||||
"28a92a86dcaf6bc9c45bb75fce4869bc0ae21c37",
|
"28a92a86dcaf6bc9c45bb75fce4869bc0ae21c37",
|
||||||
[]
|
[]
|
||||||
],
|
],
|
||||||
|
"downloadable-font-scoped-to-document-ref.html": [
|
||||||
|
"4d7da060cbf1a161aa1366f0bcb00b94e09502ad",
|
||||||
|
[]
|
||||||
|
],
|
||||||
"first-available-font-001-ref.html": [
|
"first-available-font-001-ref.html": [
|
||||||
"0acbd338e0ce9f558d2eaa2e48ad4be0524fb0ae",
|
"0acbd338e0ce9f558d2eaa2e48ad4be0524fb0ae",
|
||||||
[]
|
[]
|
||||||
|
@ -403399,6 +403416,18 @@
|
||||||
"6ed4aa506e95a35a065318f597547653bda52eb5",
|
"6ed4aa506e95a35a065318f597547653bda52eb5",
|
||||||
[]
|
[]
|
||||||
],
|
],
|
||||||
|
"iframe-missing-font-face-rule.html": [
|
||||||
|
"da97b781e8072923138e0160320e76d3013c3e53",
|
||||||
|
[]
|
||||||
|
],
|
||||||
|
"iframe-using-ahem-as-web-font.html": [
|
||||||
|
"b21066df8f57a8b11432a1168a62e7a4fbbe07b1",
|
||||||
|
[]
|
||||||
|
],
|
||||||
|
"iframe-without-web-font.html": [
|
||||||
|
"85e7fef282889894016088e082b623b92a436784",
|
||||||
|
[]
|
||||||
|
],
|
||||||
"js": {
|
"js": {
|
||||||
"font-variant-features.js": [
|
"font-variant-features.js": [
|
||||||
"4b56fee193956710b847ba79c5f9c3a5a7d15a33",
|
"4b56fee193956710b847ba79c5f9c3a5a7d15a33",
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<div style="font-size: 30px; font-family: CustomFontFamily">Hello!</div>
|
|
@ -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>
|
|
@ -0,0 +1,2 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<div style="font-size: 30px;">Hello!</div>
|
Loading…
Add table
Add a link
Reference in a new issue