servo/components/layout_2020/context.rs
Martin Robinson fe8b23d14a
layout: Add a FontMetricsProvider for resolving font-relative units (#31966)
The only font relative unit that Servo knows how to resolve currently is
`rem` (relative to the root font size). This is because Stylo cannot do
any font queries. This adds a mechanism to allow this, exposing the
ability to properly render `ex` units in Servo.

This change only allows resolving some font size relative units thoug,
as Servo doesn't collect all the FontMetrics it needs to resolve them
all. This capability will be added in followup changes.

Some new tests fail:
 - ex-unit-001.html: This test fails because Servo does not yet have
   support for setting the weight using @font-face rules on web fonts.
 - ex-unit-004.html: This test fails because Servo does not yet have
   support for setting the Unicode range of a web font using @font-face
   rules.
 - first-available-font-001.html: This test fails because the above
   two feature are missing.
2024-04-04 12:35:15 +00:00

159 lines
5.3 KiB
Rust

/* 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::cell::RefCell;
use std::sync::{Arc, Mutex};
use fnv::FnvHashMap;
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext;
use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
};
use parking_lot::{ReentrantMutex, RwLock};
use script_layout_interface::{PendingImage, PendingImageState};
use servo_url::{ImmutableOrigin, ServoUrl};
use style::context::SharedStyleContext;
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,
pub origin: ImmutableOrigin,
/// Bits shared by the layout and style system.
pub style_context: SharedStyleContext<'a>,
/// A FontContext to be used during layout.
pub font_cache_thread: Arc<ReentrantMutex<FontCacheThread>>,
/// Reference to the script thread image cache.
pub image_cache: Arc<dyn ImageCache>,
/// A list of in-progress image loads to be shared with the script thread.
pub pending_images: Mutex<Vec<PendingImage>>,
pub webrender_image_cache:
Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo>>>,
}
impl<'a> Drop for LayoutContext<'a> {
fn drop(&mut self) {
if !std::thread::panicking() {
assert!(self.pending_images.lock().unwrap().is_empty());
}
}
}
impl<'a> LayoutContext<'a> {
#[inline(always)]
pub fn shared_context(&self) -> &SharedStyleContext {
&self.style_context
}
pub fn get_or_request_image_or_meta(
&self,
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder,
) -> Option<ImageOrMetadataAvailable> {
// Check for available image or start tracking.
let cache_result = self.image_cache.get_cached_image_status(
url.clone(),
self.origin.clone(),
None,
use_placeholder,
);
match cache_result {
ImageCacheResult::Available(img_or_meta) => Some(img_or_meta),
// Image has been requested, is still pending. Return no image for this paint loop.
// When the image loads it will trigger a reflow and/or repaint.
ImageCacheResult::Pending(id) => {
let image = PendingImage {
state: PendingImageState::PendingResponse,
node: node.into(),
id,
origin: self.origin.clone(),
};
self.pending_images.lock().unwrap().push(image);
None
},
// Not yet requested - request image or metadata from the cache
ImageCacheResult::ReadyForRequest(id) => {
let image = PendingImage {
state: PendingImageState::Unrequested(url),
node: node.into(),
id,
origin: self.origin.clone(),
};
self.pending_images.lock().unwrap().push(image);
None
},
// Image failed to load, so just return nothing
ImageCacheResult::LoadError => None,
}
}
pub fn get_webrender_image_for_url(
&self,
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder,
) -> Option<WebRenderImageInfo> {
if let Some(existing_webrender_image) = self
.webrender_image_cache
.read()
.get(&(url.clone(), use_placeholder))
{
return Some(*existing_webrender_image);
}
match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) {
Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => {
let image_info = WebRenderImageInfo {
width: image.width,
height: image.height,
key: image.id,
};
if image_info.key.is_none() {
Some(image_info)
} else {
let mut webrender_image_cache = self.webrender_image_cache.write();
webrender_image_cache.insert((url, use_placeholder), image_info);
Some(image_info)
}
},
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())),
)
})
}