aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--components/canvas/canvas_data.rs43
-rw-r--r--components/canvas/canvas_paint_thread.rs10
-rw-r--r--components/gfx/Cargo.toml1
-rw-r--r--components/gfx/font.rs158
-rw-r--r--components/gfx/font_cache_thread.rs8
-rw-r--r--components/gfx/font_context.rs162
-rw-r--r--components/gfx/font_template.rs5
-rw-r--r--components/gfx/platform/freetype/font.rs78
-rw-r--r--components/gfx/platform/macos/font.rs11
-rw-r--r--components/gfx/platform/windows/font.rs7
-rw-r--r--components/gfx/tests/font_context.rs58
-rw-r--r--components/gfx/text/shaping/harfbuzz.rs6
-rw-r--r--components/gfx/text/text_run.rs9
-rw-r--r--components/layout/construct.rs29
-rw-r--r--components/layout/context.rs32
-rw-r--r--components/layout/fragment.rs22
-rw-r--r--components/layout/generated_content.rs6
-rw-r--r--components/layout/inline.rs4
-rw-r--r--components/layout/list_item.rs14
-rw-r--r--components/layout/text.rs27
-rw-r--r--components/layout_2020/context.rs30
-rw-r--r--components/layout_2020/flow/inline.rs59
-rw-r--r--components/layout_2020/flow/text_run.rs19
-rw-r--r--components/layout_thread/lib.rs25
-rw-r--r--components/layout_thread_2020/lib.rs126
27 files changed, 444 insertions, 507 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6b1530a7de0..20c65956206 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1952,6 +1952,7 @@ name = "gfx"
version = "0.0.1"
dependencies = [
"app_units",
+ "atomic_refcell",
"bitflags 2.5.0",
"byteorder",
"core-foundation",
diff --git a/Cargo.toml b/Cargo.toml
index 42123438215..f3809e5c641 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -115,6 +115,7 @@ to_shmem = { git = "https://github.com/servo/stylo", branch = "2024-04-16" }
tokio = "1"
tokio-rustls = "0.24"
tungstenite = "0.20"
+uluru = "3.0"
unicode-bidi = "0.3.15"
unicode-script = "0.5"
unicode-segmentation = "1.1.0"
diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs
index 5bb4640dfd1..0175242521e 100644
--- a/components/canvas/canvas_data.rs
+++ b/components/canvas/canvas_data.rs
@@ -2,9 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use std::cell::RefCell;
use std::mem;
-use std::sync::{Arc, Mutex};
+use std::sync::Arc;
use canvas_traits::canvas::*;
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
@@ -359,21 +358,6 @@ pub enum Filter {
Nearest,
}
-pub(crate) type CanvasFontContext = FontContext<FontCacheThread>;
-
-thread_local!(static FONT_CONTEXT: RefCell<Option<CanvasFontContext>> = RefCell::new(None));
-
-pub(crate) fn with_thread_local_font_context<F, R>(canvas_data: &CanvasData, f: F) -> R
-where
- F: FnOnce(&mut CanvasFontContext) -> R,
-{
- FONT_CONTEXT.with(|font_context| {
- f(font_context.borrow_mut().get_or_insert_with(|| {
- FontContext::new(canvas_data.font_cache_thread.lock().unwrap().clone())
- }))
- })
-}
-
pub struct CanvasData<'a> {
backend: Box<dyn Backend>,
drawtarget: Box<dyn GenericDrawTarget>,
@@ -386,7 +370,7 @@ pub struct CanvasData<'a> {
old_image_key: Option<ImageKey>,
/// An old webrender image key that can be deleted when the current epoch ends.
very_old_image_key: Option<ImageKey>,
- font_cache_thread: Mutex<FontCacheThread>,
+ font_context: Arc<FontContext<FontCacheThread>>,
}
fn create_backend() -> Box<dyn Backend> {
@@ -398,7 +382,7 @@ impl<'a> CanvasData<'a> {
size: Size2D<u64>,
webrender_api: Box<dyn WebrenderApi>,
antialias: AntialiasMode,
- font_cache_thread: FontCacheThread,
+ font_context: Arc<FontContext<FontCacheThread>>,
) -> CanvasData<'a> {
let backend = create_backend();
let draw_target = backend.create_drawtarget(size);
@@ -412,7 +396,7 @@ impl<'a> CanvasData<'a> {
image_key: None,
old_image_key: None,
very_old_image_key: None,
- font_cache_thread: Mutex::new(font_cache_thread),
+ font_context,
}
}
@@ -494,17 +478,14 @@ impl<'a> CanvasData<'a> {
let font = font_style.map_or_else(
|| load_system_font_from_style(None),
|style| {
- with_thread_local_font_context(self, |font_context| {
- let font_group = font_context.font_group(ServoArc::new(style.clone()));
- let font = font_group
- .borrow_mut()
- .first(font_context)
- .expect("couldn't find font");
- let font = font.borrow_mut();
- Font::from_bytes(font.template.data(), 0)
- .ok()
- .or_else(|| load_system_font_from_style(Some(style)))
- })
+ let font_group = self.font_context.font_group(ServoArc::new(style.clone()));
+ let font = font_group
+ .write()
+ .first(&self.font_context)
+ .expect("couldn't find font");
+ Font::from_bytes(font.template.data(), 0)
+ .ok()
+ .or_else(|| load_system_font_from_style(Some(style)))
},
);
let font = match font {
diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs
index ff9fcfaa313..2176ad8d636 100644
--- a/components/canvas/canvas_paint_thread.rs
+++ b/components/canvas/canvas_paint_thread.rs
@@ -4,6 +4,7 @@
use std::borrow::ToOwned;
use std::collections::HashMap;
+use std::sync::Arc;
use std::thread;
use canvas_traits::canvas::*;
@@ -11,6 +12,7 @@ use canvas_traits::ConstellationCanvasMsg;
use crossbeam_channel::{select, unbounded, Sender};
use euclid::default::Size2D;
use gfx::font_cache_thread::FontCacheThread;
+use gfx::font_context::FontContext;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use log::warn;
@@ -40,7 +42,7 @@ pub struct CanvasPaintThread<'a> {
canvases: HashMap<CanvasId, CanvasData<'a>>,
next_canvas_id: CanvasId,
webrender_api: Box<dyn WebrenderApi>,
- font_cache_thread: FontCacheThread,
+ font_context: Arc<FontContext<FontCacheThread>>,
}
impl<'a> CanvasPaintThread<'a> {
@@ -52,7 +54,7 @@ impl<'a> CanvasPaintThread<'a> {
canvases: HashMap::new(),
next_canvas_id: CanvasId(0),
webrender_api,
- font_cache_thread,
+ font_context: Arc::new(FontContext::new(font_cache_thread)),
}
}
@@ -129,8 +131,6 @@ impl<'a> CanvasPaintThread<'a> {
AntialiasMode::None
};
- let font_cache_thread = self.font_cache_thread.clone();
-
let canvas_id = self.next_canvas_id;
self.next_canvas_id.0 += 1;
@@ -138,7 +138,7 @@ impl<'a> CanvasPaintThread<'a> {
size,
self.webrender_api.clone(),
antialias,
- font_cache_thread,
+ self.font_context.clone(),
);
self.canvases.insert(canvas_id, canvas_data);
diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml
index c9c0d388fcb..bec2264c44c 100644
--- a/components/gfx/Cargo.toml
+++ b/components/gfx/Cargo.toml
@@ -15,6 +15,7 @@ doctest = false
[dependencies]
app_units = { workspace = true }
+atomic_refcell = { workspace = true }
bitflags = { workspace = true }
cssparser = { workspace = true }
euclid = { workspace = true }
diff --git a/components/gfx/font.rs b/components/gfx/font.rs
index 1921edc1d12..de42f6c321c 100644
--- a/components/gfx/font.rs
+++ b/components/gfx/font.rs
@@ -3,11 +3,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::borrow::ToOwned;
-use std::cell::RefCell;
use std::collections::HashMap;
-use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering};
-use std::sync::Arc;
+use std::sync::{Arc, OnceLock};
use std::time::Instant;
use std::{iter, str};
@@ -15,6 +13,7 @@ use app_units::Au;
use bitflags::bitflags;
use euclid::default::{Point2D, Rect, Size2D};
use log::debug;
+use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use servo_atoms::{atom, Atom};
use smallvec::SmallVec;
@@ -185,15 +184,21 @@ impl<'a> From<&'a FontStyleStruct> for FontDescriptor {
}
}
+#[derive(Debug, Default)]
+struct CachedShapeData {
+ glyph_advances: HashMap<GlyphId, FractionalPixel>,
+ glyph_indices: HashMap<char, Option<GlyphId>>,
+ shaped_text: HashMap<ShapeCacheEntry, Arc<GlyphStore>>,
+}
+
#[derive(Debug)]
pub struct Font {
pub handle: PlatformFont,
pub template: FontTemplateRef,
pub metrics: FontMetrics,
pub descriptor: FontDescriptor,
- shaper: Option<Shaper>,
- shape_cache: RefCell<HashMap<ShapeCacheEntry, Arc<GlyphStore>>>,
- glyph_advance_cache: RefCell<HashMap<u32, FractionalPixel>>,
+ shaper: OnceLock<Shaper>,
+ cached_shape_data: RwLock<CachedShapeData>,
pub font_key: FontInstanceKey,
/// If this is a synthesized small caps font, then this font reference is for
@@ -214,11 +219,10 @@ impl Font {
Ok(Font {
handle,
template,
- shaper: None,
+ shaper: OnceLock::new(),
descriptor,
metrics,
- shape_cache: RefCell::new(HashMap::new()),
- glyph_advance_cache: RefCell::new(HashMap::new()),
+ cached_shape_data: Default::default(),
font_key: FontInstanceKey::default(),
synthesized_small_caps,
})
@@ -272,52 +276,49 @@ struct ShapeCacheEntry {
}
impl Font {
- pub fn shape_text(&mut self, text: &str, options: &ShapingOptions) -> Arc<GlyphStore> {
+ pub fn shape_text(&self, text: &str, options: &ShapingOptions) -> Arc<GlyphStore> {
let this = self as *const Font;
- let mut shaper = self.shaper.take();
-
let lookup_key = ShapeCacheEntry {
text: text.to_owned(),
options: *options,
};
- let result = self
- .shape_cache
- .borrow_mut()
- .entry(lookup_key)
- .or_insert_with(|| {
- let start_time = Instant::now();
- let mut glyphs = GlyphStore::new(
- text.len(),
- options
- .flags
- .contains(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG),
- options.flags.contains(ShapingFlags::RTL_FLAG),
- );
-
- if self.can_do_fast_shaping(text, options) {
- debug!("shape_text: Using ASCII fast path.");
- self.shape_text_fast(text, options, &mut glyphs);
- } else {
- debug!("shape_text: Using Harfbuzz.");
- if shaper.is_none() {
- shaper = Some(Shaper::new(this));
- }
- shaper
- .as_ref()
- .unwrap()
- .shape_text(text, options, &mut glyphs);
- }
+ {
+ let cache = self.cached_shape_data.read();
+ if let Some(shaped_text) = cache.shaped_text.get(&lookup_key) {
+ return shaped_text.clone();
+ }
+ }
- let end_time = Instant::now();
- TEXT_SHAPING_PERFORMANCE_COUNTER.fetch_add(
- (end_time.duration_since(start_time).as_nanos()) as usize,
- Ordering::Relaxed,
- );
- Arc::new(glyphs)
- })
- .clone();
- self.shaper = shaper;
- result
+ let start_time = Instant::now();
+ let mut glyphs = GlyphStore::new(
+ text.len(),
+ options
+ .flags
+ .contains(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG),
+ options.flags.contains(ShapingFlags::RTL_FLAG),
+ );
+
+ if self.can_do_fast_shaping(text, options) {
+ debug!("shape_text: Using ASCII fast path.");
+ self.shape_text_fast(text, options, &mut glyphs);
+ } else {
+ debug!("shape_text: Using Harfbuzz.");
+ self.shaper
+ .get_or_init(|| Shaper::new(this))
+ .shape_text(text, options, &mut glyphs);
+ }
+
+ let shaped_text = Arc::new(glyphs);
+ let mut cache = self.cached_shape_data.write();
+ cache.shaped_text.insert(lookup_key, shaped_text.clone());
+
+ let end_time = Instant::now();
+ TEXT_SHAPING_PERFORMANCE_COUNTER.fetch_add(
+ (end_time.duration_since(start_time).as_nanos()) as usize,
+ Ordering::Relaxed,
+ );
+
+ shaped_text
}
fn can_do_fast_shaping(&self, text: &str, options: &ShapingOptions) -> bool {
@@ -377,11 +378,21 @@ impl Font {
#[inline]
pub fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
+ {
+ let cache = self.cached_shape_data.read();
+ if let Some(glyph) = cache.glyph_indices.get(&codepoint) {
+ return *glyph;
+ }
+ }
let codepoint = match self.descriptor.variant {
font_variant_caps::T::SmallCaps => codepoint.to_ascii_uppercase(),
font_variant_caps::T::Normal => codepoint,
};
- self.handle.glyph_index(codepoint)
+ let glyph_index = self.handle.glyph_index(codepoint);
+
+ let mut cache = self.cached_shape_data.write();
+ cache.glyph_indices.insert(codepoint, glyph_index);
+ glyph_index
}
pub fn has_glyph_for(&self, codepoint: char) -> bool {
@@ -392,21 +403,27 @@ impl Font {
self.handle.glyph_h_kerning(first_glyph, second_glyph)
}
- pub fn glyph_h_advance(&self, glyph: GlyphId) -> FractionalPixel {
- *self
- .glyph_advance_cache
- .borrow_mut()
- .entry(glyph)
- .or_insert_with(|| {
- match self.handle.glyph_h_advance(glyph) {
- Some(adv) => adv,
- None => LAST_RESORT_GLYPH_ADVANCE as FractionalPixel, // FIXME: Need fallback strategy
- }
- })
+ pub fn glyph_h_advance(&self, glyph_id: GlyphId) -> FractionalPixel {
+ {
+ let cache = self.cached_shape_data.read();
+ if let Some(width) = cache.glyph_advances.get(&glyph_id) {
+ return *width;
+ }
+ }
+
+ // TODO: Need a fallback strategy.
+ let new_width = match self.handle.glyph_h_advance(glyph_id) {
+ Some(adv) => adv,
+ None => LAST_RESORT_GLYPH_ADVANCE as FractionalPixel,
+ };
+
+ let mut cache = self.cached_shape_data.write();
+ cache.glyph_advances.insert(glyph_id, new_width);
+ new_width
}
}
-pub type FontRef = Rc<RefCell<Font>>;
+pub type FontRef = Arc<Font>;
/// A `FontGroup` is a prioritised list of fonts for a given set of font styles. It is used by
/// `TextRun` to decide which font to render a character with. If none of the fonts listed in the
@@ -442,14 +459,13 @@ impl FontGroup {
/// found, returns None.
pub fn find_by_codepoint<S: FontSource>(
&mut self,
- font_context: &mut FontContext<S>,
+ font_context: &FontContext<S>,
codepoint: char,
) -> Option<FontRef> {
let should_look_for_small_caps = self.descriptor.variant == font_variant_caps::T::SmallCaps &&
codepoint.is_ascii_lowercase();
let font_or_synthesized_small_caps = |font: FontRef| {
if should_look_for_small_caps {
- let font = font.borrow();
if font.synthesized_small_caps.is_some() {
return font.synthesized_small_caps.clone();
}
@@ -457,7 +473,7 @@ impl FontGroup {
Some(font)
};
- let glyph_in_font = |font: &FontRef| font.borrow().has_glyph_for(codepoint);
+ let glyph_in_font = |font: &FontRef| font.has_glyph_for(codepoint);
let char_in_template =
|template: FontTemplateRef| template.char_in_unicode_range(codepoint);
@@ -466,7 +482,7 @@ impl FontGroup {
}
if let Some(ref last_matching_fallback) = self.last_matching_fallback {
- if char_in_template(last_matching_fallback.borrow().template.clone()) &&
+ if char_in_template(last_matching_fallback.template.clone()) &&
glyph_in_font(last_matching_fallback)
{
return font_or_synthesized_small_caps(last_matching_fallback.clone());
@@ -487,7 +503,7 @@ impl FontGroup {
}
/// Find the first available font in the group, or the first available fallback font.
- pub fn first<S: FontSource>(&mut self, font_context: &mut FontContext<S>) -> Option<FontRef> {
+ pub fn first<S: FontSource>(&mut self, font_context: &FontContext<S>) -> Option<FontRef> {
// From https://drafts.csswg.org/css-fonts/#first-available-font:
// > The first available font, used for example in the definition of font-relative lengths
// > such as ex or in the definition of the line-height property, is defined to be the first
@@ -506,7 +522,7 @@ impl FontGroup {
/// a suitable font.
fn find<S, TemplatePredicate, FontPredicate>(
&mut self,
- font_context: &mut FontContext<S>,
+ font_context: &FontContext<S>,
template_predicate: TemplatePredicate,
font_predicate: FontPredicate,
) -> Option<FontRef>
@@ -535,7 +551,7 @@ impl FontGroup {
/// used to refine the list of family names which will be tried.
fn find_fallback<S, TemplatePredicate, FontPredicate>(
&mut self,
- font_context: &mut FontContext<S>,
+ font_context: &FontContext<S>,
codepoint: Option<char>,
template_predicate: TemplatePredicate,
font_predicate: FontPredicate,
@@ -602,7 +618,7 @@ impl FontGroupFamily {
fn find<S, TemplatePredicate, FontPredicate>(
&mut self,
font_descriptor: &FontDescriptor,
- font_context: &mut FontContext<S>,
+ font_context: &FontContext<S>,
template_predicate: &TemplatePredicate,
font_predicate: &FontPredicate,
) -> Option<FontRef>
@@ -633,7 +649,7 @@ impl FontGroupFamily {
fn members<'a, S: FontSource>(
&'a mut self,
font_descriptor: &FontDescriptor,
- font_context: &mut FontContext<S>,
+ font_context: &FontContext<S>,
) -> impl Iterator<Item = &mut FontGroupFamilyMember> + 'a {
let family_descriptor = &self.family_descriptor;
let members = self.members.get_or_insert_with(|| {
diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs
index 5d7d160ca65..3c1790edb6f 100644
--- a/components/gfx/font_cache_thread.rs
+++ b/components/gfx/font_cache_thread.rs
@@ -3,14 +3,13 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::borrow::ToOwned;
-use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::{Deref, RangeInclusive};
-use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::{f32, fmt, mem, thread};
use app_units::Au;
+use atomic_refcell::AtomicRefCell;
use gfx_traits::WebrenderApi;
use ipc_channel::ipc::{self, IpcBytesSender, IpcReceiver, IpcSender};
use log::{debug, trace};
@@ -125,7 +124,8 @@ impl FontTemplates {
return;
}
}
- self.templates.push(Rc::new(RefCell::new(new_template)));
+ self.templates
+ .push(Arc::new(AtomicRefCell::new(new_template)));
}
}
@@ -730,7 +730,7 @@ impl FontSource for FontCacheThread {
.into_iter()
.map(|serialized_font_template| {
let font_data = serialized_font_template.bytes_receiver.recv().ok();
- Rc::new(RefCell::new(FontTemplate {
+ Arc::new(AtomicRefCell::new(FontTemplate {
identifier: serialized_font_template.identifier,
descriptor: serialized_font_template.descriptor.clone(),
data: font_data.map(Arc::new),
diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs
index b831fc916cd..c08764f7a70 100644
--- a/components/gfx/font_context.rs
+++ b/components/gfx/font_context.rs
@@ -2,17 +2,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use std::cell::RefCell;
use std::collections::HashMap;
use std::default::Default;
use std::hash::{BuildHasherDefault, Hash, Hasher};
-use std::rc::Rc;
-use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
use app_units::Au;
use fnv::FnvHasher;
use log::debug;
-use servo_arc::Arc;
+use parking_lot::{Mutex, RwLock};
+use servo_arc::Arc as ServoArc;
use style::computed_values::font_variant_caps::T as FontVariantCaps;
use style::properties::style_structs::Font as FontStyleStruct;
use webrender_api::{FontInstanceFlags, FontInstanceKey};
@@ -25,10 +24,6 @@ use crate::platform::core_text_font_cache::CoreTextFontCache;
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
-/// An epoch for the font context cache. The cache is flushed if the current epoch does not match
-/// this one.
-static FONT_CACHE_EPOCH: AtomicUsize = AtomicUsize::new(0);
-
pub trait FontSource {
fn get_font_instance(
&mut self,
@@ -49,48 +44,48 @@ pub trait FontSource {
/// required.
#[derive(Debug)]
pub struct FontContext<S: FontSource> {
- font_source: S,
+ font_source: Mutex<S>,
// TODO: The font context holds a strong ref to the cached fonts
// so they will never be released. Find out a good time to drop them.
// See bug https://github.com/servo/servo/issues/3300
- font_cache: HashMap<FontCacheKey, Option<FontRef>>,
- font_template_cache: HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>,
-
+ font_cache: RwLock<HashMap<FontCacheKey, Option<FontRef>>>,
+ font_template_cache: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
font_group_cache:
- HashMap<FontGroupCacheKey, Rc<RefCell<FontGroup>>, BuildHasherDefault<FnvHasher>>,
-
- epoch: usize,
+ RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>,
}
impl<S: FontSource> FontContext<S> {
pub fn new(font_source: S) -> FontContext<S> {
#[allow(clippy::default_constructed_unit_structs)]
FontContext {
- font_source,
- font_cache: HashMap::new(),
- font_template_cache: HashMap::new(),
- font_group_cache: HashMap::with_hasher(Default::default()),
- epoch: 0,
+ font_source: Mutex::new(font_source),
+ font_cache: RwLock::default(),
+ font_template_cache: RwLock::default(),
+ font_group_cache: RwLock::default(),
}
}
- fn expire_font_caches_if_necessary(&mut self) {
- let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst);
- if current_epoch == self.epoch {
- return;
- }
-
- self.font_cache.clear();
- self.font_template_cache.clear();
- self.font_group_cache.clear();
- self.epoch = current_epoch
+ /// Invalidate all caches that this [`FontContext`] holds and any in-process platform-specific
+ /// caches.
+ ///
+ /// # Safety
+ ///
+ /// This should never be called when more than one thread is using the [`FontContext`] or it
+ /// may leave the context in an inconsistent state.
+ pub fn invalidate_caches(&self) {
+ #[cfg(target_os = "macos")]
+ CoreTextFontCache::clear_core_text_font_cache();
+
+ self.font_cache.write().clear();
+ self.font_template_cache.write().clear();
+ self.font_group_cache.write().clear();
}
/// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`.
/// Font groups are cached, so subsequent calls with the same `style` will return a reference
/// to an existing `FontGroup`.
- pub fn font_group(&mut self, style: Arc<FontStyleStruct>) -> Rc<RefCell<FontGroup>> {
+ pub fn font_group(&self, style: ServoArc<FontStyleStruct>) -> Arc<RwLock<FontGroup>> {
let font_size = style.font_size.computed_size().into();
self.font_group_with_size(style, font_size)
}
@@ -98,27 +93,27 @@ impl<S: FontSource> FontContext<S> {
/// Like [`Self::font_group`], but overriding the size found in the [`FontStyleStruct`] with the given size
/// in pixels.
pub fn font_group_with_size(
- &mut self,
- style: Arc<FontStyleStruct>,
+ &self,
+ style: ServoArc<FontStyleStruct>,
size: Au,
- ) -> Rc<RefCell<FontGroup>> {
- self.expire_font_caches_if_necessary();
-
+ ) -> Arc<RwLock<FontGroup>> {
let cache_key = FontGroupCacheKey { size, style };
- if let Some(font_group) = self.font_group_cache.get(&cache_key) {
+ if let Some(font_group) = self.font_group_cache.read().get(&cache_key) {
return font_group.clone();
}
- let font_group = Rc::new(RefCell::new(FontGroup::new(&cache_key.style)));
- self.font_group_cache.insert(cache_key, 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
/// reference to the same underlying `Font`.
pub fn font(
- &mut self,
+ &self,
font_template: FontTemplateRef,
font_descriptor: &FontDescriptor,
) -> Option<FontRef> {
@@ -130,7 +125,7 @@ impl<S: FontSource> FontContext<S> {
}
fn get_font_maybe_synthesizing_small_caps(
- &mut self,
+ &self,
font_template: FontTemplateRef,
font_descriptor: &FontDescriptor,
synthesize_small_caps: bool,
@@ -157,27 +152,28 @@ impl<S: FontSource> FontContext<S> {
font_descriptor: font_descriptor.clone(),
};
- self.font_cache.get(&cache_key).cloned().unwrap_or_else(|| {
- debug!(
- "FontContext::font cache miss for font_template={:?} font_descriptor={:?}",
- font_template, font_descriptor
- );
-
- let font = self
- .create_font(
- font_template,
- font_descriptor.to_owned(),
- synthesized_small_caps_font,
- )
- .ok();
- self.font_cache.insert(cache_key, font.clone());
+ if let Some(font) = self.font_cache.read().get(&cache_key).cloned() {
+ return font;
+ }
- font
- })
+ debug!(
+ "FontContext::font cache miss for font_template={:?} font_descriptor={:?}",
+ font_template, font_descriptor
+ );
+
+ let font = self
+ .create_font(
+ font_template,
+ font_descriptor.to_owned(),
+ synthesized_small_caps_font,
+ )
+ .ok();
+ self.font_cache.write().insert(cache_key, font.clone());
+ font
}
pub fn matching_templates(
- &mut self,
+ &self,
descriptor_to_match: &FontDescriptor,
family_descriptor: &FontFamilyDescriptor,
) -> Vec<FontTemplateRef> {
@@ -186,27 +182,31 @@ impl<S: FontSource> FontContext<S> {
family_descriptor: family_descriptor.clone(),
};
- self.font_template_cache.get(&cache_key).cloned().unwrap_or_else(|| {
- debug!(
- "FontContext::font_template cache miss for template_descriptor={:?} family_descriptor={:?}",
- descriptor_to_match,
- family_descriptor
- );
-
- let template_info = self.font_source.find_matching_font_templates(
- descriptor_to_match,
- family_descriptor.clone(),
- );
-
- self.font_template_cache.insert(cache_key, template_info.clone());
- template_info
- })
+ if let Some(templates) = self.font_template_cache.read().get(&cache_key).cloned() {
+ return templates;
+ }
+
+ debug!(
+ "FontContext::font_template cache miss for template_descriptor={:?} family_descriptor={:?}",
+ descriptor_to_match,
+ family_descriptor
+ );
+
+ let templates = self
+ .font_source
+ .lock()
+ .find_matching_font_templates(descriptor_to_match, family_descriptor.clone());
+
+ self.font_template_cache
+ .write()
+ .insert(cache_key, templates.clone());
+ templates
}
/// Create a `Font` for use in layout calculations, from a `FontTemplateData` returned by the
/// cache thread and a `FontDescriptor` which contains the styling parameters.
fn create_font(
- &mut self,
+ &self,
font_template: FontTemplateRef,
font_descriptor: FontDescriptor,
synthesized_small_caps: Option<FontRef>,
@@ -216,13 +216,13 @@ impl<S: FontSource> FontContext<S> {
font_descriptor.clone(),
synthesized_small_caps,
)?;
- font.font_key = self.font_source.get_font_instance(
+ font.font_key = self.font_source.lock().get_font_instance(
font_template.identifier(),
font_descriptor.pt_size,
font.webrender_font_instance_flags(),
);
- Ok(Rc::new(RefCell::new(font)))
+ Ok(Arc::new(font))
}
}
@@ -240,7 +240,7 @@ struct FontTemplateCacheKey {
#[derive(Debug)]
struct FontGroupCacheKey {
- style: Arc<FontStyleStruct>,
+ style: ServoArc<FontStyleStruct>,
size: Au,
}
@@ -260,11 +260,3 @@ impl Hash for FontGroupCacheKey {
self.style.hash.hash(hasher)
}
}
-
-#[inline]
-pub fn invalidate_font_caches() {
- FONT_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst);
-
- #[cfg(target_os = "macos")]
- CoreTextFontCache::clear_core_text_font_cache();
-}
diff --git a/components/gfx/font_template.rs b/components/gfx/font_template.rs
index 30a40608df5..0af8aa75443 100644
--- a/components/gfx/font_template.rs
+++ b/components/gfx/font_template.rs
@@ -2,12 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use std::cell::RefCell;
use std::fmt::{Debug, Error, Formatter};
use std::ops::RangeInclusive;
-use std::rc::Rc;
use std::sync::Arc;
+use atomic_refcell::AtomicRefCell;
use serde::{Deserialize, Serialize};
use servo_url::ServoUrl;
use style::computed_values::font_stretch::T as FontStretch;
@@ -22,7 +21,7 @@ use crate::platform::font::PlatformFont;
use crate::platform::font_list::LocalFontIdentifier;
/// A reference to a [`FontTemplate`] with shared ownership and mutability.
-pub type FontTemplateRef = Rc<RefCell<FontTemplate>>;
+pub type FontTemplateRef = Arc<AtomicRefCell<FontTemplate>>;
/// Describes how to select a font from a given family. This is very basic at the moment and needs
/// to be expanded or refactored when we support more of the font styling parameters.
diff --git a/components/gfx/platform/freetype/font.rs b/components/gfx/platform/freetype/font.rs
index f437dbed8c0..f68e370e6d3 100644
--- a/components/gfx/platform/freetype/font.rs
+++ b/components/gfx/platform/freetype/font.rs
@@ -9,14 +9,15 @@ use std::{mem, ptr};
use app_units::Au;
use freetype::freetype::{
- FT_Done_Face, FT_F26Dot6, FT_Face, FT_FaceRec, FT_Get_Char_Index, FT_Get_Kerning,
- FT_Get_Sfnt_Table, FT_GlyphSlot, FT_Int32, FT_Kerning_Mode, FT_Load_Glyph, FT_Load_Sfnt_Table,
- FT_Long, FT_New_Memory_Face, FT_Set_Char_Size, FT_Sfnt_Tag, FT_SizeRec, FT_Size_Metrics,
- FT_UInt, FT_ULong, FT_Vector, FT_STYLE_FLAG_ITALIC,
+ FT_Done_Face, FT_F26Dot6, FT_Face, FT_Get_Char_Index, FT_Get_Kerning, FT_Get_Sfnt_Table,
+ FT_GlyphSlot, FT_Int32, FT_Kerning_Mode, FT_Load_Glyph, FT_Load_Sfnt_Table, FT_Long,
+ FT_New_Memory_Face, FT_Set_Char_Size, FT_Sfnt_Tag, FT_SizeRec, FT_Size_Metrics, FT_UInt,
+ FT_ULong, FT_Vector, FT_STYLE_FLAG_ITALIC,
};
use freetype::succeeded;
use freetype::tt_os2::TT_OS2;
use log::debug;
+use parking_lot::ReentrantMutex;
use style::computed_values::font_stretch::T as FontStretch;
use style::computed_values::font_weight::T as FontWeight;
use style::values::computed::font::FontStyle;
@@ -75,19 +76,26 @@ pub struct PlatformFont {
/// The font data itself, which must stay valid for the lifetime of the
/// platform [`FT_Face`].
font_data: Arc<Vec<u8>>,
- face: FT_Face,
+ face: ReentrantMutex<FT_Face>,
can_do_fast_shaping: bool,
}
+// FT_Face can be used in multiple threads, but from only one thread at a time.
+// It's protected with a ReentrantMutex for PlatformFont.
+// See https://freetype.org/freetype2/docs/reference/ft2-face_creation.html#ft_face.
+unsafe impl Sync for PlatformFont {}
+unsafe impl Send for PlatformFont {}
+
impl Drop for PlatformFont {
fn drop(&mut self) {
- assert!(!self.face.is_null());
+ let face = self.face.lock();
+ assert!(!face.is_null());
unsafe {
// The FreeType documentation says that both `FT_New_Face` and `FT_Done_Face`
// should be protected by a mutex.
// See https://freetype.org/freetype2/docs/reference/ft2-library_setup.html.
let _guard = FreeTypeLibraryHandle::get().lock();
- if !succeeded(FT_Done_Face(self.face)) {
+ if !succeeded(FT_Done_Face(*face)) {
panic!("FT_Done_Face failed");
}
}
@@ -134,7 +142,7 @@ impl PlatformFontMethods for PlatformFont {
) -> Result<PlatformFont, &'static str> {
let face = create_face(data.clone(), face_index, pt_size)?;
let mut handle = PlatformFont {
- face,
+ face: ReentrantMutex::new(face),
font_data: data,
can_do_fast_shaping: false,
};
@@ -147,7 +155,8 @@ impl PlatformFontMethods for PlatformFont {
}
fn descriptor(&self) -> FontTemplateDescriptor {
- let style = if unsafe { (*self.face).style_flags & FT_STYLE_FLAG_ITALIC as c_long != 0 } {
+ let face = self.face.lock();
+ let style = if unsafe { (**face).style_flags & FT_STYLE_FLAG_ITALIC as c_long != 0 } {
FontStyle::ITALIC
} else {
FontStyle::NORMAL
@@ -178,9 +187,11 @@ impl PlatformFontMethods for PlatformFont {
}
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
- assert!(!self.face.is_null());
+ let face = self.face.lock();
+ assert!(!face.is_null());
+
unsafe {
- let idx = FT_Get_Char_Index(self.face, codepoint as FT_ULong);
+ let idx = FT_Get_Char_Index(*face, codepoint as FT_ULong);
if idx != 0 as FT_UInt {
Some(idx as GlyphId)
} else {
@@ -194,11 +205,13 @@ impl PlatformFontMethods for PlatformFont {
}
fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
- assert!(!self.face.is_null());
+ let face = self.face.lock();
+ assert!(!face.is_null());
+
let mut delta = FT_Vector { x: 0, y: 0 };
unsafe {
FT_Get_Kerning(
- self.face,
+ *face,
first_glyph,
second_glyph,
FT_Kerning_Mode::FT_KERNING_DEFAULT as FT_UInt,
@@ -213,11 +226,13 @@ impl PlatformFontMethods for PlatformFont {
}
fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
- assert!(!self.face.is_null());
+ let face = self.face.lock();
+ assert!(!face.is_null());
+
unsafe {
- let res = FT_Load_Glyph(self.face, glyph as FT_UInt, GLYPH_LOAD_FLAGS);
+ let res = FT_Load_Glyph(*face, glyph as FT_UInt, GLYPH_LOAD_FLAGS);
if succeeded(res) {
- let void_glyph = (*self.face).glyph;
+ let void_glyph = (**face).glyph;
let slot: FT_GlyphSlot = void_glyph;
assert!(!slot.is_null());
let advance = (*slot).metrics.horiAdvance;
@@ -232,8 +247,8 @@ impl PlatformFontMethods for PlatformFont {
}
fn metrics(&self) -> FontMetrics {
- /* TODO(Issue #76): complete me */
- let face = self.face_rec_mut();
+ let face = self.face.lock();
+ let face = unsafe { **face };
let underline_size = self.font_units_to_au(face.underline_thickness as f64);
let underline_offset = self.font_units_to_au(face.underline_position as f64);
@@ -294,24 +309,19 @@ impl PlatformFontMethods for PlatformFont {
}
fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
+ let face = self.face.lock();
let tag = tag as FT_ULong;
unsafe {
// Get the length
let mut len = 0;
- if !succeeded(FT_Load_Sfnt_Table(
- self.face,
- tag,
- 0,
- ptr::null_mut(),
- &mut len,
- )) {
+ if !succeeded(FT_Load_Sfnt_Table(*face, tag, 0, ptr::null_mut(), &mut len)) {
return None;
}
// Get the bytes
let mut buf = vec![0u8; len as usize];
if !succeeded(FT_Load_Sfnt_Table(
- self.face,
+ *face,
tag,
0,
buf.as_mut_ptr(),
@@ -343,9 +353,10 @@ impl<'a> PlatformFont {
}
fn has_table(&self, tag: FontTableTag) -> bool {
+ let face = self.face.lock();
unsafe {
succeeded(FT_Load_Sfnt_Table(
- self.face,
+ *face,
tag as FT_ULong,
0,
ptr::null_mut(),
@@ -354,13 +365,9 @@ impl<'a> PlatformFont {
}
}
- #[allow(clippy::mut_from_ref)] // Intended for this function
- fn face_rec_mut(&'a self) -> &'a mut FT_FaceRec {
- unsafe { &mut (*self.face) }
- }
-
fn font_units_to_au(&self, value: f64) -> Au {
- let face = self.face_rec_mut();
+ let face = self.face.lock();
+ let face = unsafe { **face };
// face.size is a *c_void in the bindings, presumably to avoid
// recursive structural types
@@ -377,9 +384,10 @@ impl<'a> PlatformFont {
}
fn os2_table(&self) -> Option<OS2Table> {
+ let face = self.face.lock();
+
unsafe {
- let os2 =
- FT_Get_Sfnt_Table(self.face_rec_mut(), FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2;
+ let os2 = FT_Get_Sfnt_Table(*face, FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2;
let valid = !os2.is_null() && (*os2).version != 0xffff;
if !valid {
diff --git a/components/gfx/platform/macos/font.rs b/components/gfx/platform/macos/font.rs
index c6e009ffae8..7ab00b51cd0 100644
--- a/components/gfx/platform/macos/font.rs
+++ b/components/gfx/platform/macos/font.rs
@@ -63,6 +63,17 @@ pub struct PlatformFont {
can_do_fast_shaping: bool,
}
+// From https://developer.apple.com/documentation/coretext:
+// > All individual functions in Core Text are thread-safe. Font objects (CTFont,
+// > CTFontDescriptor, and associated objects) can be used simultaneously by multiple
+// > operations, work queues, or threads. However, the layout objects (CTTypesetter,
+// > CTFramesetter, CTRun, CTLine, CTFrame, and associated objects) should be used in a
+// > single operation, work queue, or thread.
+//
+// The other element is a read-only CachedKernTable which is stored in a CFData.
+unsafe impl Sync for PlatformFont {}
+unsafe impl Send for PlatformFont {}
+
impl PlatformFont {
/// Cache all the data needed for basic horizontal kerning. This is used only as a fallback or
/// fast path (when the GPOS table is missing or unnecessary) so it needn't handle every case.
diff --git a/components/gfx/platform/windows/font.rs b/components/gfx/platform/windows/font.rs
index 5debd0c64fa..5ec0a4c658a 100644
--- a/components/gfx/platform/windows/font.rs
+++ b/components/gfx/platform/windows/font.rs
@@ -82,6 +82,13 @@ pub struct PlatformFont {
scaled_du_to_px: f32,
}
+// Based on information from the Skia codebase, it seems that DirectWrite APIs from
+// Windows 10 and beyond are thread safe. If problems arise from this, we can protect the
+// platform font with a Mutex.
+// See https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/src/ports/SkScalerContext_win_dw.cpp;l=56;bpv=0;bpt=1.
+unsafe impl Sync for PlatformFont {}
+unsafe impl Send for PlatformFont {}
+
struct Nondebug<T>(T);
impl<T> fmt::Debug for Nondebug<T> {
diff --git a/components/gfx/tests/font_context.rs b/components/gfx/tests/font_context.rs
index f502b7d97fe..54babf3ef83 100644
--- a/components/gfx/tests/font_context.rs
+++ b/components/gfx/tests/font_context.rs
@@ -150,22 +150,26 @@ fn font_family(names: Vec<&str>) -> FontFamily {
#[test]
fn test_font_group_is_cached_by_style() {
let source = TestFontSource::new();
- let mut context = FontContext::new(source);
+ let context = FontContext::new(source);
let style1 = style();
let mut style2 = style();
style2.set_font_style(FontStyle::ITALIC);
- assert_eq!(
- context.font_group(Arc::new(style1.clone())).as_ptr(),
- context.font_group(Arc::new(style1.clone())).as_ptr(),
+ assert!(
+ std::ptr::eq(
+ &*context.font_group(Arc::new(style1.clone())).read(),
+ &*context.font_group(Arc::new(style1.clone())).read()
+ ),
"the same font group should be returned for two styles with the same hash"
);
- assert_ne!(
- context.font_group(Arc::new(style1.clone())).as_ptr(),
- context.font_group(Arc::new(style2.clone())).as_ptr(),
+ assert!(
+ !std::ptr::eq(
+ &*context.font_group(Arc::new(style1.clone())).read(),
+ &*context.font_group(Arc::new(style2.clone())).read()
+ ),
"different font groups should be returned for two styles with different hashes"
)
}
@@ -181,12 +185,9 @@ fn test_font_group_find_by_codepoint() {
let group = context.font_group(Arc::new(style));
- let font = group
- .borrow_mut()
- .find_by_codepoint(&mut context, 'a')
- .unwrap();
+ let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
assert_eq!(
- font.borrow().identifier(),
+ font.identifier(),
TestFontSource::identifier_for_font_name("csstest-ascii")
);
assert_eq!(
@@ -195,12 +196,9 @@ fn test_font_group_find_by_codepoint() {
"only the first font in the list should have been loaded"
);
- let font = group
- .borrow_mut()
- .find_by_codepoint(&mut context, 'a')
- .unwrap();
+ let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
assert_eq!(
- font.borrow().identifier(),
+ font.identifier(),
TestFontSource::identifier_for_font_name("csstest-ascii")
);
assert_eq!(
@@ -209,12 +207,9 @@ fn test_font_group_find_by_codepoint() {
"we shouldn't load the same font a second time"
);
- let font = group
- .borrow_mut()
- .find_by_codepoint(&mut context, 'á')
- .unwrap();
+ let font = group.write().find_by_codepoint(&mut context, 'á').unwrap();
assert_eq!(
- font.borrow().identifier(),
+ font.identifier(),
TestFontSource::identifier_for_font_name("csstest-basic-regular")
);
assert_eq!(count.get(), 2, "both fonts should now have been loaded");
@@ -230,22 +225,16 @@ fn test_font_fallback() {
let group = context.font_group(Arc::new(style));
- let font = group
- .borrow_mut()
- .find_by_codepoint(&mut context, 'a')
- .unwrap();
+ let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
assert_eq!(
- font.borrow().identifier(),
+ font.identifier(),
TestFontSource::identifier_for_font_name("csstest-ascii"),
"a family in the group should be used if there is a matching glyph"
);
- let font = group
- .borrow_mut()
- .find_by_codepoint(&mut context, 'á')
- .unwrap();
+ let font = group.write().find_by_codepoint(&mut context, 'á').unwrap();
assert_eq!(
- font.borrow().identifier(),
+ font.identifier(),
TestFontSource::identifier_for_font_name("csstest-basic-regular"),
"a fallback font should be used if there is no matching glyph in the group"
);
@@ -255,7 +244,7 @@ fn test_font_fallback() {
fn test_font_template_is_cached() {
let source = TestFontSource::new();
let count = source.find_font_count.clone();
- let mut context = FontContext::new(source);
+ let context = FontContext::new(source);
let mut font_descriptor = FontDescriptor {
weight: FontWeight::normal(),
@@ -280,8 +269,7 @@ fn test_font_template_is_cached() {
.unwrap();
assert_ne!(
- font1.borrow().descriptor.pt_size,
- font2.borrow().descriptor.pt_size,
+ font1.descriptor.pt_size, font2.descriptor.pt_size,
"the same font should not have been returned"
);
diff --git a/components/gfx/text/shaping/harfbuzz.rs b/components/gfx/text/shaping/harfbuzz.rs
index 5f2e801f40a..ce0ff44be8f 100644
--- a/components/gfx/text/shaping/harfbuzz.rs
+++ b/components/gfx/text/shaping/harfbuzz.rs
@@ -130,6 +130,12 @@ pub struct Shaper {
font: *const Font,
}
+// The HarfBuzz API is thread safe as well as our `Font`, so we can make the data
+// structures here as thread-safe as well. This doesn't seem to be documented,
+// but was expressed as one of the original goals of the HarfBuzz API.
+unsafe impl Sync for Shaper {}
+unsafe impl Send for Shaper {}
+
impl Drop for Shaper {
fn drop(&mut self) {
unsafe {
diff --git a/components/gfx/text/text_run.rs b/components/gfx/text/text_run.rs
index 3973702a026..d4bec79564a 100644
--- a/components/gfx/text/text_run.rs
+++ b/components/gfx/text/text_run.rs
@@ -16,7 +16,7 @@ use unicode_bidi as bidi;
use webrender_api::FontInstanceKey;
use xi_unicode::LineBreakLeafIter;
-use crate::font::{Font, FontMetrics, RunMetrics, ShapingFlags, ShapingOptions};
+use crate::font::{FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
use crate::text::glyph::{ByteIndex, GlyphStore};
thread_local! {
@@ -180,13 +180,14 @@ impl<'a> Iterator for CharacterSliceIterator<'a> {
impl<'a> TextRun {
/// Constructs a new text run. Also returns if there is a line break at the beginning
pub fn new(
- font: &mut Font,
+ font: FontRef,
text: String,
options: &ShapingOptions,
bidi_level: bidi::Level,
breaker: &mut Option<LineBreakLeafIter>,
) -> (TextRun, bool) {
- let (glyphs, break_at_zero) = TextRun::break_and_shape(font, &text, options, breaker);
+ let (glyphs, break_at_zero) =
+ TextRun::break_and_shape(font.clone(), &text, options, breaker);
(
TextRun {
text: Arc::new(text),
@@ -202,7 +203,7 @@ impl<'a> TextRun {
}
pub fn break_and_shape(
- font: &mut Font,
+ font: FontRef,
text: &str,
options: &ShapingOptions,
breaker: &mut Option<LineBreakLeafIter>,
diff --git a/components/layout/construct.rs b/components/layout/construct.rs
index 522635543fd..1c4eff55898 100644
--- a/components/layout/construct.rs
+++ b/components/layout/construct.rs
@@ -42,7 +42,7 @@ use style::values::generics::counters::ContentItem;
use style::LocalName;
use crate::block::BlockFlow;
-use crate::context::{with_thread_local_font_context, LayoutContext};
+use crate::context::LayoutContext;
use crate::data::{InnerLayoutData, LayoutDataFlags};
use crate::display_list::items::OpaqueNode;
use crate::flex::FlexFlow;
@@ -517,11 +517,10 @@ where
// We must scan for runs before computing minimum ascent and descent because scanning
// for runs might collapse so much whitespace away that only hypothetical fragments
// remain. In that case the inline flow will compute its ascent and descent to be zero.
- let scanned_fragments =
- with_thread_local_font_context(self.layout_context, |font_context| {
- TextRunScanner::new()
- .scan_for_runs(font_context, mem::take(&mut fragments.fragments))
- });
+ let scanned_fragments = TextRunScanner::new().scan_for_runs(
+ &self.layout_context.font_context,
+ mem::take(&mut fragments.fragments),
+ );
let mut inline_flow_ref = FlowRef::new(Arc::new(InlineFlow::from_fragments(
scanned_fragments,
node.style(self.style_context()).writing_mode,
@@ -550,11 +549,10 @@ where
{
// FIXME(#6503): Use Arc::get_mut().unwrap() here.
let inline_flow = FlowRef::deref_mut(&mut inline_flow_ref).as_mut_inline();
- inline_flow.minimum_line_metrics =
- with_thread_local_font_context(self.layout_context, |font_context| {
- inline_flow
- .minimum_line_metrics(font_context, &node.style(self.style_context()))
- });
+ inline_flow.minimum_line_metrics = inline_flow.minimum_line_metrics(
+ &self.layout_context.font_context,
+ &node.style(self.style_context()),
+ );
}
inline_flow_ref.finish();
@@ -1545,11 +1543,10 @@ where
)),
self.layout_context,
));
- let marker_fragments =
- with_thread_local_font_context(self.layout_context, |font_context| {
- TextRunScanner::new()
- .scan_for_runs(font_context, unscanned_marker_fragments)
- });
+ let marker_fragments = TextRunScanner::new().scan_for_runs(
+ &self.layout_context.font_context,
+ unscanned_marker_fragments,
+ );
marker_fragments.fragments
},
ListStyleTypeContent::GeneratedContent(info) => vec![Fragment::new(
diff --git a/components/layout/context.rs b/components/layout/context.rs
index d43210c141b..5cedf5d38aa 100644
--- a/components/layout/context.rs
+++ b/components/layout/context.rs
@@ -4,7 +4,6 @@
//! Data needed by layout.
-use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::sync::{Arc, Mutex};
@@ -13,7 +12,6 @@ use std::thread;
use fnv::FnvHasher;
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext;
-use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
@@ -29,32 +27,6 @@ use crate::display_list::items::{OpaqueNode, WebRenderImageInfo};
pub type LayoutFontContext = FontContext<FontCacheThread>;
-thread_local!(static FONT_CONTEXT_KEY: RefCell<Option<LayoutFontContext>> = RefCell::new(None));
-
-pub fn with_thread_local_font_context<F, R>(layout_context: &LayoutContext, f: F) -> R
-where
- F: FnOnce(&mut LayoutFontContext) -> R,
-{
- FONT_CONTEXT_KEY.with(|k| {
- let mut font_context = k.borrow_mut();
- if font_context.is_none() {
- let font_cache_thread = layout_context.font_cache_thread.lock().unwrap().clone();
- *font_context = Some(FontContext::new(font_cache_thread));
- }
- f(&mut RefMut::map(font_context, |x| x.as_mut().unwrap()))
- })
-}
-
-pub fn malloc_size_of_persistent_local_context(ops: &mut MallocSizeOfOps) -> usize {
- FONT_CONTEXT_KEY.with(|r| {
- if let Some(ref context) = *r.borrow() {
- context.size_of(ops)
- } else {
- 0
- }
- })
-}
-
type WebrenderImageCache =
HashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo, BuildHasherDefault<FnvHasher>>;
@@ -72,8 +44,8 @@ pub struct LayoutContext<'a> {
/// Reference to the script thread image cache.
pub image_cache: Arc<dyn ImageCache>,
- /// Interface to the font cache thread.
- pub font_cache_thread: Mutex<FontCacheThread>,
+ /// A FontContext to be used during layout.
+ pub font_context: Arc<FontContext<FontCacheThread>>,
/// A cache of WebRender image info.
pub webrender_image_cache: Arc<RwLock<WebrenderImageCache>>,
diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs
index 8e50d1c499e..45843627493 100644
--- a/components/layout/fragment.rs
+++ b/components/layout/fragment.rs
@@ -57,7 +57,7 @@ use style::values::generics::transform;
use webrender_api::units::LayoutTransform;
use webrender_api::{self, ImageKey};
-use crate::context::{with_thread_local_font_context, LayoutContext};
+use crate::context::LayoutContext;
use crate::display_list::items::{ClipScrollNodeIndex, OpaqueNode, BLUR_INFLATION_FACTOR};
use crate::display_list::ToLayout;
use crate::floats::ClearType;
@@ -852,9 +852,8 @@ impl Fragment {
))),
);
unscanned_ellipsis_fragments.push_back(ellipsis_fragment);
- let ellipsis_fragments = with_thread_local_font_context(layout_context, |font_context| {
- TextRunScanner::new().scan_for_runs(font_context, unscanned_ellipsis_fragments)
- });
+ let ellipsis_fragments = TextRunScanner::new()
+ .scan_for_runs(&layout_context.font_context, unscanned_ellipsis_fragments);
debug_assert_eq!(ellipsis_fragments.len(), 1);
ellipsis_fragment = ellipsis_fragments.fragments.into_iter().next().unwrap();
ellipsis_fragment.flags |= FragmentFlags::IS_ELLIPSIS;
@@ -2346,9 +2345,10 @@ impl Fragment {
return InlineMetrics::new(Au(0), Au(0), Au(0));
}
// See CSS 2.1 § 10.8.1.
- let font_metrics = with_thread_local_font_context(layout_context, |font_context| {
- text::font_metrics_for_style(font_context, self_.style.clone_font())
- });
+ let font_metrics = text::font_metrics_for_style(
+ &layout_context.font_context,
+ self_.style.clone_font(),
+ );
let line_height = text::line_height_from_style(&self_.style, &font_metrics);
InlineMetrics::from_font_metrics(&info.run.font_metrics, line_height)
}
@@ -2426,10 +2426,10 @@ impl Fragment {
VerticalAlign::Keyword(kw) => match kw {
VerticalAlignKeyword::Baseline => {},
VerticalAlignKeyword::Middle => {
- let font_metrics =
- with_thread_local_font_context(layout_context, |font_context| {
- text::font_metrics_for_style(font_context, self.style.clone_font())
- });
+ let font_metrics = text::font_metrics_for_style(
+ &layout_context.font_context,
+ self.style.clone_font(),
+ );
offset += (content_inline_metrics.ascent -
content_inline_metrics.space_below_baseline -
font_metrics.x_height)
diff --git a/components/layout/generated_content.rs b/components/layout/generated_content.rs
index 862e8c88191..3a50024d522 100644
--- a/components/layout/generated_content.rs
+++ b/components/layout/generated_content.rs
@@ -20,7 +20,7 @@ use style::servo::restyle_damage::ServoRestyleDamage;
use style::values::generics::counters::ContentItem;
use style::values::specified::list::{QuotePair, Quotes};
-use crate::context::{with_thread_local_font_context, LayoutContext};
+use crate::context::LayoutContext;
use crate::display_list::items::OpaqueNode;
use crate::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils};
use crate::fragment::{
@@ -493,9 +493,7 @@ fn render_text(
));
// FIXME(pcwalton): This should properly handle multiple marker fragments. This could happen
// due to text run splitting.
- let fragments = with_thread_local_font_context(layout_context, |font_context| {
- TextRunScanner::new().scan_for_runs(font_context, fragments)
- });
+ let fragments = TextRunScanner::new().scan_for_runs(&layout_context.font_context, fragments);
if fragments.is_empty() {
None
} else {
diff --git a/components/layout/inline.rs b/components/layout/inline.rs
index fa2ebb01efb..5926e4b2e03 100644
--- a/components/layout/inline.rs
+++ b/components/layout/inline.rs
@@ -1239,7 +1239,7 @@ impl InlineFlow {
/// `style` is the style of the block.
pub fn minimum_line_metrics(
&self,
- font_context: &mut LayoutFontContext,
+ font_context: &LayoutFontContext,
style: &ComputedValues,
) -> LineMetrics {
InlineFlow::minimum_line_metrics_for_fragments(
@@ -1255,7 +1255,7 @@ impl InlineFlow {
/// `style` is the style of the block that these fragments belong to.
pub fn minimum_line_metrics_for_fragments(
fragments: &[Fragment],
- font_context: &mut LayoutFontContext,
+ font_context: &LayoutFontContext,
style: &ComputedValues,
) -> LineMetrics {
// As a special case, if this flow contains only hypothetical fragments, then the entire
diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs
index 601046fc0c8..a7c44164ac1 100644
--- a/components/layout/list_item.rs
+++ b/components/layout/list_item.rs
@@ -14,7 +14,7 @@ use style::properties::ComputedValues;
use style::servo::restyle_damage::ServoRestyleDamage;
use crate::block::BlockFlow;
-use crate::context::{with_thread_local_font_context, LayoutContext};
+use crate::context::LayoutContext;
use crate::display_list::items::DisplayListSection;
use crate::display_list::{
BorderPaintingMode, DisplayListBuildState, StackingContextCollectionState,
@@ -114,13 +114,11 @@ impl ListItemFlow {
fn assign_marker_block_sizes(&mut self, layout_context: &LayoutContext) {
// FIXME(pcwalton): Do this during flow construction, like `InlineFlow` does?
- let marker_line_metrics = with_thread_local_font_context(layout_context, |font_context| {
- InlineFlow::minimum_line_metrics_for_fragments(
- &self.marker_fragments,
- font_context,
- &self.block_flow.fragment.style,
- )
- });
+ let marker_line_metrics = InlineFlow::minimum_line_metrics_for_fragments(
+ &self.marker_fragments,
+ &layout_context.font_context,
+ &self.block_flow.fragment.style,
+ );
for marker in &mut self.marker_fragments {
marker.assign_replaced_block_size_if_necessary();
diff --git a/components/layout/text.rs b/components/layout/text.rs
index b11a25f8dee..2875ccd0d77 100644
--- a/components/layout/text.rs
+++ b/components/layout/text.rs
@@ -70,7 +70,7 @@ impl TextRunScanner {
pub fn scan_for_runs(
&mut self,
- font_context: &mut LayoutFontContext,
+ font_context: &LayoutFontContext,
mut fragments: LinkedList<Fragment>,
) -> InlineFragments {
debug!(
@@ -150,7 +150,7 @@ impl TextRunScanner {
/// be adjusted.
fn flush_clump_to_list(
&mut self,
- font_context: &mut LayoutFontContext,
+ font_context: &LayoutFontContext,
out_fragments: &mut Vec<Fragment>,
paragraph_bytes_processed: &mut usize,
bidi_levels: Option<&[bidi::Level]>,
@@ -203,10 +203,9 @@ impl TextRunScanner {
.map(|l| l.into())
.unwrap_or_else(|| {
let space_width = font_group
- .borrow_mut()
+ .write()
.find_by_codepoint(font_context, ' ')
.and_then(|font| {
- let font = font.borrow();
font.glyph_index(' ')
.map(|glyph_id| font.glyph_h_advance(glyph_id))
})
@@ -248,7 +247,7 @@ impl TextRunScanner {
for (byte_index, character) in text.char_indices() {
if !character.is_control() {
let font = font_group
- .borrow_mut()
+ .write()
.find_by_codepoint(font_context, character);
let bidi_level = match bidi_levels {
@@ -367,7 +366,7 @@ impl TextRunScanner {
// If no font is found (including fallbacks), there's no way we can render.
let font = match run_info
.font
- .or_else(|| font_group.borrow_mut().first(font_context))
+ .or_else(|| font_group.write().first(font_context))
{
Some(font) => font,
None => {
@@ -377,7 +376,7 @@ impl TextRunScanner {
};
let (run, break_at_zero) = TextRun::new(
- &mut font.borrow_mut(),
+ font,
run_info.text,
&options,
run_info.bidi_level,
@@ -535,14 +534,12 @@ fn bounding_box_for_run_metrics(
/// Panics if no font can be found for the given font style.
#[inline]
pub fn font_metrics_for_style(
- font_context: &mut LayoutFontContext,
+ font_context: &LayoutFontContext,
style: crate::ServoArc<FontStyleStruct>,
) -> FontMetrics {
let font_group = font_context.font_group(style);
- let font = font_group.borrow_mut().first(font_context);
- let font = font.as_ref().unwrap().borrow();
-
- font.metrics.clone()
+ let font = font_group.write().first(font_context);
+ font.as_ref().unwrap().metrics.clone()
}
/// Returns the line block-size needed by the given computed style and font size.
@@ -664,10 +661,8 @@ impl RunInfo {
fn has_font(&self, font: &Option<FontRef>) -> bool {
fn identifier_and_pt_size(font: &Option<FontRef>) -> Option<(FontIdentifier, Au)> {
- font.as_ref().map(|font| {
- let font = font.borrow();
- (font.identifier().clone(), font.descriptor.pt_size)
- })
+ font.as_ref()
+ .map(|font| (font.identifier().clone(), font.descriptor.pt_size))
}
identifier_and_pt_size(&self.font) == identifier_and_pt_size(font)
diff --git a/components/layout_2020/context.rs b/components/layout_2020/context.rs
index 8ffc3386e3e..500f16093b1 100644
--- a/components/layout_2020/context.rs
+++ b/components/layout_2020/context.rs
@@ -2,7 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use std::cell::RefCell;
use std::sync::{Arc, Mutex};
use fnv::FnvHashMap;
@@ -12,7 +11,7 @@ use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
};
-use parking_lot::{ReentrantMutex, RwLock};
+use parking_lot::RwLock;
use script_layout_interface::{PendingImage, PendingImageState};
use servo_url::{ImmutableOrigin, ServoUrl};
use style::context::SharedStyleContext;
@@ -20,8 +19,6 @@ use style::dom::OpaqueNode;
use crate::display_list::WebRenderImageInfo;
-thread_local!(static FONT_CONTEXT: RefCell<Option<FontContext<FontCacheThread>>> = RefCell::new(None));
-
pub struct LayoutContext<'a> {
pub id: PipelineId,
pub use_rayon: bool,
@@ -31,7 +28,7 @@ pub struct LayoutContext<'a> {
pub style_context: SharedStyleContext<'a>,
/// A FontContext to be used during layout.
- pub font_cache_thread: Arc<ReentrantMutex<FontCacheThread>>,
+ pub font_context: Arc<FontContext<FontCacheThread>>,
/// Reference to the script thread image cache.
pub image_cache: Arc<dyn ImageCache>,
@@ -133,27 +130,4 @@ impl<'a> LayoutContext<'a> {
None | Some(ImageOrMetadataAvailable::MetadataAvailable(_)) => None,
}
}
-
- pub fn with_font_context<F, R>(&self, callback: F) -> R
- where
- F: FnOnce(&mut FontContext<FontCacheThread>) -> R,
- {
- with_thread_local_font_context(&self.font_cache_thread, callback)
- }
-}
-
-pub fn with_thread_local_font_context<F, R>(
- font_cache_thread: &ReentrantMutex<FontCacheThread>,
- callback: F,
-) -> R
-where
- F: FnOnce(&mut FontContext<FontCacheThread>) -> R,
-{
- FONT_CONTEXT.with(|font_context| {
- callback(
- font_context
- .borrow_mut()
- .get_or_insert_with(|| FontContext::new(font_cache_thread.lock().clone())),
- )
- })
}
diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs
index cadf5840e32..047cfc7087d 100644
--- a/components/layout_2020/flow/inline.rs
+++ b/components/layout_2020/flow/inline.rs
@@ -1638,10 +1638,9 @@ impl InlineFormattingContext {
// It's unfortunate that it isn't possible to get this during IFC text processing, but in
// that situation the style of the containing block is unknown.
- let default_font_metrics = layout_context.with_font_context(|font_context| {
- get_font_for_first_font_for_style(style, font_context)
- .map(|font| font.borrow().metrics.clone())
- });
+ let default_font_metrics =
+ get_font_for_first_font_for_style(style, &layout_context.font_context)
+ .map(|font| font.metrics.clone());
let style_text = containing_block.style.get_inherited_text();
let mut ifc = InlineFormattingContextState {
@@ -1768,34 +1767,30 @@ impl InlineFormattingContext {
// For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary.
let mut on_word_boundary = true;
- layout_context.with_font_context(|font_context| {
- let mut linebreaker = None;
- self.foreach(|iter_item| match iter_item {
- InlineFormattingContextIterItem::Item(InlineLevelBox::TextRun(
- ref mut text_run,
- )) => {
- text_run.break_and_shape(
- font_context,
- &mut linebreaker,
- &mut ifc_fonts,
- &mut last_inline_box_ended_with_white_space,
- &mut on_word_boundary,
- );
- },
- InlineFormattingContextIterItem::Item(InlineLevelBox::InlineBox(inline_box)) => {
- if let Some(font) =
- get_font_for_first_font_for_style(&inline_box.style, font_context)
- {
- inline_box.default_font_index =
- Some(add_or_get_font(&font, &mut ifc_fonts));
- }
- },
- InlineFormattingContextIterItem::Item(InlineLevelBox::Atomic(_)) => {
- last_inline_box_ended_with_white_space = false;
- on_word_boundary = true;
- },
- _ => {},
- });
+ let mut linebreaker = None;
+ self.foreach(|iter_item| match iter_item {
+ InlineFormattingContextIterItem::Item(InlineLevelBox::TextRun(ref mut text_run)) => {
+ text_run.break_and_shape(
+ &layout_context.font_context,
+ &mut linebreaker,
+ &mut ifc_fonts,
+ &mut last_inline_box_ended_with_white_space,
+ &mut on_word_boundary,
+ );
+ },
+ InlineFormattingContextIterItem::Item(InlineLevelBox::InlineBox(inline_box)) => {
+ if let Some(font) = get_font_for_first_font_for_style(
+ &inline_box.style,
+ &layout_context.font_context,
+ ) {
+ inline_box.default_font_index = Some(add_or_get_font(&font, &mut ifc_fonts));
+ }
+ },
+ InlineFormattingContextIterItem::Item(InlineLevelBox::Atomic(_)) => {
+ last_inline_box_ended_with_white_space = false;
+ on_word_boundary = true;
+ },
+ _ => {},
});
self.font_metrics = ifc_fonts;
diff --git a/components/layout_2020/flow/text_run.rs b/components/layout_2020/flow/text_run.rs
index 86884565d57..e82d1a6159b 100644
--- a/components/layout_2020/flow/text_run.rs
+++ b/components/layout_2020/flow/text_run.rs
@@ -111,7 +111,7 @@ impl TextRunSegment {
/// compatible with this segment or false otherwise.
fn update_if_compatible(
&mut self,
- font: &FontRef,
+ new_font: &FontRef,
script: Script,
fonts: &[FontKeyAndMetrics],
) -> bool {
@@ -120,7 +120,6 @@ impl TextRunSegment {
}
let current_font_key_and_metrics = &fonts[self.font_index];
- let new_font = font.borrow();
if new_font.font_key != current_font_key_and_metrics.key ||
new_font.descriptor.pt_size != current_font_key_and_metrics.pt_size
{
@@ -208,7 +207,7 @@ impl TextRun {
pub(super) fn break_and_shape(
&mut self,
- font_context: &mut FontContext<FontCacheThread>,
+ font_context: &FontContext<FontCacheThread>,
linebreaker: &mut Option<LineBreakLeafIter>,
font_cache: &mut Vec<FontKeyAndMetrics>,
last_inline_box_ended_with_collapsible_white_space: &mut bool,
@@ -244,7 +243,6 @@ impl TextRun {
let segments = segment_results
.into_iter()
.map(|(mut segment, font)| {
- let mut font = font.borrow_mut();
let word_spacing = style_word_spacing.unwrap_or_else(|| {
let space_width = font
.glyph_index(' ')
@@ -260,7 +258,7 @@ impl TextRun {
};
(segment.runs, segment.break_at_start) =
gfx::text::text_run::TextRun::break_and_shape(
- &mut font,
+ font,
&self.text
[segment.range.begin().0 as usize..segment.range.end().0 as usize],
&shaping_options,
@@ -280,7 +278,7 @@ impl TextRun {
/// [`super::InlineFormattingContext`].
fn segment_text(
&mut self,
- font_context: &mut FontContext<FontCacheThread>,
+ font_context: &FontContext<FontCacheThread>,
font_cache: &mut Vec<FontKeyAndMetrics>,
last_inline_box_ended_with_collapsible_white_space: &mut bool,
on_word_boundary: &mut bool,
@@ -341,7 +339,7 @@ impl TextRun {
}
let font = match font_group
- .borrow_mut()
+ .write()
.find_by_codepoint(font_context, character)
{
Some(font) => font,
@@ -383,7 +381,7 @@ impl TextRun {
// Either we have a current segment or we only had control character and whitespace. In both
// of those cases, just use the first font.
if current.is_none() {
- current = font_group.borrow_mut().first(font_context).map(|font| {
+ current = font_group.write().first(font_context).map(|font| {
let font_index = add_or_get_font(&font, font_cache);
(
TextRunSegment::new(font_index, Script::Common, ByteIndex(0)),
@@ -489,7 +487,6 @@ fn char_does_not_change_font(character: char) -> bool {
}
pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec<FontKeyAndMetrics>) -> usize {
- let font = font.borrow();
for (index, ifc_font_info) in ifc_fonts.iter().enumerate() {
if ifc_font_info.key == font.font_key && ifc_font_info.pt_size == font.descriptor.pt_size {
return index;
@@ -505,11 +502,11 @@ pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec<FontKeyAndMetr
pub(super) fn get_font_for_first_font_for_style(
style: &ComputedValues,
- font_context: &mut FontContext<FontCacheThread>,
+ font_context: &FontContext<FontCacheThread>,
) -> Option<FontRef> {
let font = font_context
.font_group(style.clone_font())
- .borrow_mut()
+ .write()
.first(font_context);
if font.is_none() {
warn!("Could not find font for style: {:?}", style.clone_font());
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs
index ccd0f37d829..589a2744364 100644
--- a/components/layout_thread/lib.rs
+++ b/components/layout_thread/lib.rs
@@ -22,16 +22,15 @@ use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as
use euclid::{Point2D, Rect, Scale, Size2D};
use fnv::FnvHashMap;
use fxhash::{FxHashMap, FxHashSet};
+use gfx::font;
use gfx::font_cache_thread::FontCacheThread;
-use gfx::{font, font_context};
+use gfx::font_context::FontContext;
use gfx_traits::{node_id_from_scroll_id, Epoch};
use histogram::Histogram;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use layout::construct::ConstructionResult;
-use layout::context::{
- malloc_size_of_persistent_local_context, LayoutContext, RegisteredPainter, RegisteredPainters,
-};
+use layout::context::{LayoutContext, RegisteredPainter, RegisteredPainters};
use layout::display_list::items::{DisplayList, ScrollOffsetMap, WebRenderImageInfo};
use layout::display_list::{IndexableText, ToLayout};
use layout::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils, MutableOwnedFlowUtils};
@@ -136,6 +135,9 @@ pub struct LayoutThread {
/// Public interface to the font cache thread.
font_cache_thread: FontCacheThread,
+ /// A FontContext to be used during layout.
+ font_context: Arc<FontContext<FontCacheThread>>,
+
/// Is this the first reflow in this LayoutThread?
first_reflow: Cell<bool>,
@@ -513,7 +515,7 @@ impl Layout for LayoutThread {
// malloc_enclosing_size_of function.
let mut ops = MallocSizeOfOps::new(servo_allocator::usable_size, None, None);
- // FIXME(njn): Just measuring the display tree for now.
+ // TODO: Measure more than just display list, stylist, and font context.
let display_list = self.display_list.borrow();
let display_list_ref = display_list.as_ref();
let formatted_url = &format!("url({})", self.url);
@@ -528,13 +530,6 @@ impl Layout for LayoutThread {
kind: ReportKind::ExplicitJemallocHeapSize,
size: self.stylist.size_of(&mut ops),
});
-
- // The LayoutThread has data in Persistent TLS...
- reports.push(Report {
- path: path![formatted_url, "layout-thread", "local-context"],
- kind: ReportKind::ExplicitJemallocHeapSize,
- size: malloc_size_of_persistent_local_context(&mut ops),
- });
}
fn reflow(&mut self, script_reflow: script_layout_interface::ScriptReflow) {
@@ -573,6 +568,7 @@ impl LayoutThread {
// Let webrender know about this pipeline by sending an empty display list.
webrender_api.send_initial_transaction(id.into());
+ let font_context = Arc::new(FontContext::new(font_cache_thread.clone()));
let device = Device::new(
MediaType::screen(),
QuirksMode::NoQuirks,
@@ -602,6 +598,7 @@ impl LayoutThread {
registered_painters: RegisteredPaintersImpl(Default::default()),
image_cache,
font_cache_thread,
+ font_context,
first_reflow: Cell::new(true),
font_cache_sender: ipc_font_cache_sender,
parallel_flag: true,
@@ -675,7 +672,7 @@ impl LayoutThread {
traversal_flags,
),
image_cache: self.image_cache.clone(),
- font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
+ font_context: self.font_context.clone(),
webrender_image_cache: self.webrender_image_cache.clone(),
pending_images: Mutex::new(vec![]),
registered_painters: &self.registered_painters,
@@ -726,7 +723,7 @@ impl LayoutThread {
}
fn handle_web_font_loaded(&self) {
- font_context::invalidate_font_caches();
+ self.font_context.invalidate_caches();
self.script_chan
.send(ConstellationControlMsg::WebFontLoaded(self.id))
.unwrap();
diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs
index d0a9eccfef9..c23e6f55fd4 100644
--- a/components/layout_thread_2020/lib.rs
+++ b/components/layout_thread_2020/lib.rs
@@ -22,7 +22,7 @@ use euclid::{Point2D, Scale, Size2D, Vector2D};
use fnv::FnvHashMap;
use fxhash::FxHashMap;
use gfx::font_cache_thread::FontCacheThread;
-use gfx::font_context;
+use gfx::font_context::FontContext;
use gfx_traits::{node_id_from_scroll_id, Epoch};
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
@@ -41,7 +41,7 @@ use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
use msg::constellation_msg::{BrowsingContextId, PipelineId};
use net_traits::image_cache::{ImageCache, UsePlaceholder};
-use parking_lot::{ReentrantMutex, RwLock};
+use parking_lot::RwLock;
use profile_traits::mem::{Report, ReportKind};
use profile_traits::path;
use profile_traits::time::{
@@ -121,7 +121,10 @@ pub struct LayoutThread {
/// 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: Arc<ReentrantMutex<FontCacheThread>>,
+ font_cache_thread: FontCacheThread,
+
+ /// A FontContext to be used during layout.
+ font_context: Arc<FontContext<FontCacheThread>>,
/// Is this the first reflow in this LayoutThread?
first_reflow: Cell<bool>,
@@ -484,13 +487,13 @@ impl LayoutThread {
// The device pixel ratio is incorrect (it does not have the hidpi value),
// but it will be set correctly when the initial reflow takes place.
- let font_cache_thread = Arc::new(ReentrantMutex::new(font_cache_thread));
+ let font_context = Arc::new(FontContext::new(font_cache_thread.clone()));
let device = Device::new(
MediaType::screen(),
QuirksMode::NoQuirks,
window_size.initial_viewport,
window_size.device_pixel_ratio,
- Box::new(LayoutFontMetricsProvider(font_cache_thread.clone())),
+ Box::new(LayoutFontMetricsProvider(font_context.clone())),
);
// Ask the router to proxy IPC messages from the font cache thread to layout.
@@ -514,6 +517,7 @@ impl LayoutThread {
registered_painters: RegisteredPaintersImpl(Default::default()),
image_cache,
font_cache_thread,
+ font_context,
first_reflow: Cell::new(true),
font_cache_sender: ipc_font_cache_sender,
generation: Cell::new(0),
@@ -584,7 +588,7 @@ impl LayoutThread {
traversal_flags,
),
image_cache: self.image_cache.clone(),
- font_cache_thread: self.font_cache_thread.clone(),
+ font_context: self.font_context.clone(),
webrender_image_cache: self.webrender_image_cache.clone(),
pending_images: Mutex::new(vec![]),
use_rayon,
@@ -616,10 +620,8 @@ impl LayoutThread {
// Find all font-face rules and notify the font cache of them.
// GWTODO: Need to handle unloading web fonts.
if stylesheet.is_effective_for_device(self.stylist.device(), guard) {
- let newly_loading_font_count = self
- .font_cache_thread
- .lock()
- .add_all_web_fonts_from_stylesheet(
+ let newly_loading_font_count =
+ self.font_cache_thread.add_all_web_fonts_from_stylesheet(
stylesheet,
guard,
self.stylist.device(),
@@ -637,7 +639,7 @@ impl LayoutThread {
}
fn handle_web_font_loaded(&self) {
- font_context::invalidate_font_caches();
+ self.font_context.invalidate_caches();
self.script_chan
.send(ConstellationControlMsg::WebFontLoaded(self.id))
.unwrap();
@@ -1076,7 +1078,7 @@ impl LayoutThread {
self.stylist.quirks_mode(),
window_size_data.initial_viewport,
window_size_data.device_pixel_ratio,
- Box::new(LayoutFontMetricsProvider(self.font_cache_thread.clone())),
+ Box::new(LayoutFontMetricsProvider(self.font_context.clone())),
);
// Preserve any previously computed root font size.
@@ -1235,7 +1237,7 @@ impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
}
#[derive(Debug)]
-struct LayoutFontMetricsProvider(Arc<ReentrantMutex<FontCacheThread>>);
+struct LayoutFontMetricsProvider(Arc<FontContext<FontCacheThread>>);
impl FontMetricsProvider for LayoutFontMetricsProvider {
fn query_font_metrics(
@@ -1246,55 +1248,55 @@ impl FontMetricsProvider for LayoutFontMetricsProvider {
_in_media_query: bool,
_retrieve_math_scales: bool,
) -> FontMetrics {
- layout::context::with_thread_local_font_context(&self.0, move |font_context| {
- let font_group =
- font_context.font_group_with_size(ServoArc::new(font.clone()), base_size.into());
- let Some(first_font_metrics) = font_group
- .borrow_mut()
- .first(font_context)
- .map(|font| font.borrow().metrics.clone())
- else {
- return Default::default();
- };
+ let font_context = &self.0;
+ let font_group = self
+ .0
+ .font_group_with_size(ServoArc::new(font.clone()), base_size.into());
+
+ let Some(first_font_metrics) = font_group
+ .write()
+ .first(font_context)
+ .map(|font| font.metrics.clone())
+ else {
+ return Default::default();
+ };
- // Only use the x-height of this font if it is non-zero. Some fonts return
- // inaccurate metrics, which shouldn't be used.
- let x_height = Some(first_font_metrics.x_height)
- .filter(|x_height| !x_height.is_zero())
- .map(CSSPixelLength::from);
-
- let zero_advance_measure = first_font_metrics
- .zero_horizontal_advance
- .or_else(|| {
- font_group
- .borrow_mut()
- .find_by_codepoint(font_context, '0')?
- .borrow()
- .metrics
- .zero_horizontal_advance
- })
- .map(CSSPixelLength::from);
- let ic_width = first_font_metrics
- .ic_horizontal_advance
- .or_else(|| {
- font_group
- .borrow_mut()
- .find_by_codepoint(font_context, '\u{6C34}')?
- .borrow()
- .metrics
- .ic_horizontal_advance
- })
- .map(CSSPixelLength::from);
-
- FontMetrics {
- x_height,
- zero_advance_measure,
- cap_height: None,
- ic_width,
- ascent: first_font_metrics.ascent.into(),
- script_percent_scale_down: None,
- script_script_percent_scale_down: None,
- }
- })
+ // Only use the x-height of this font if it is non-zero. Some fonts return
+ // inaccurate metrics, which shouldn't be used.
+ let x_height = Some(first_font_metrics.x_height)
+ .filter(|x_height| !x_height.is_zero())
+ .map(CSSPixelLength::from);
+
+ let zero_advance_measure = first_font_metrics
+ .zero_horizontal_advance
+ .or_else(|| {
+ font_group
+ .write()
+ .find_by_codepoint(font_context, '0')?
+ .metrics
+ .zero_horizontal_advance
+ })
+ .map(CSSPixelLength::from);
+
+ let ic_width = first_font_metrics
+ .ic_horizontal_advance
+ .or_else(|| {
+ font_group
+ .write()
+ .find_by_codepoint(font_context, '\u{6C34}')?
+ .metrics
+ .ic_horizontal_advance
+ })
+ .map(CSSPixelLength::from);
+
+ FontMetrics {
+ x_height,
+ zero_advance_measure,
+ cap_height: None,
+ ic_width,
+ ascent: first_font_metrics.ascent.into(),
+ script_percent_scale_down: None,
+ script_script_percent_scale_down: None,
+ }
}
}