Address some code nits

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-05-27 12:11:51 +02:00
parent 2ab511f19c
commit cbb2d18bc0
11 changed files with 166 additions and 148 deletions

View file

@ -16,8 +16,9 @@ use malloc_size_of::{MallocSizeOf as MallocSizeOfTrait, MallocSizeOfOps};
use malloc_size_of_derive::MallocSizeOf; use malloc_size_of_derive::MallocSizeOf;
use mime::Mime; use mime::Mime;
use net_traits::image_cache::{ use net_traits::image_cache::{
Image, ImageCache, ImageCacheMessage, ImageCacheResult, ImageLoadListener, Image, ImageCache, ImageCacheResponseMessage, ImageCacheResult, ImageLoadListener,
ImageOrMetadataAvailable, ImageResponse, PendingImageId, UsePlaceholder, VectorImage, ImageOrMetadataAvailable, ImageResponse, PendingImageId, RasterizationCompleteResponse,
UsePlaceholder, VectorImage,
}; };
use net_traits::request::CorsSettings; use net_traits::request::CorsSettings;
use net_traits::{FetchMetadata, FetchResponseMsg, FilteredMetadata, NetworkError}; use net_traits::{FetchMetadata, FetchResponseMsg, FilteredMetadata, NetworkError};
@ -397,41 +398,39 @@ impl PendingLoad {
} }
} }
#[derive(MallocSizeOf)] #[derive(Default, MallocSizeOf)]
struct RasterizationTask { struct RasterizationTask {
listeners: Vec<(PipelineId, IpcSender<ImageCacheMessage>)>, listeners: Vec<(PipelineId, IpcSender<ImageCacheResponseMessage>)>,
result: Option<RasterImage>, result: Option<RasterImage>,
} }
// ====================================================================== /// ## Image cache implementation.
// Image cache implementation.
// ======================================================================
#[derive(MallocSizeOf)] #[derive(MallocSizeOf)]
struct ImageCacheStore { struct ImageCacheStore {
// Images that are loading over network, or decoding. /// Images that are loading over network, or decoding.
pending_loads: AllPendingLoads, pending_loads: AllPendingLoads,
// Images that have finished loading (successful or not) /// Images that have finished loading (successful or not)
completed_loads: HashMap<ImageKey, CompletedLoad>, completed_loads: HashMap<ImageKey, CompletedLoad>,
// Vector (e.g. SVG) images that have been sucessfully loaded and parsed /// Vector (e.g. SVG) images that have been sucessfully loaded and parsed
// but are yet to be rasterized. Since the same SVG data can be used for /// but are yet to be rasterized. Since the same SVG data can be used for
// rasterizing at different sizes, we use this hasmap to share the data. /// rasterizing at different sizes, we use this hasmap to share the data.
vector_images: HashMap<PendingImageId, VectorImageData>, vector_images: HashMap<PendingImageId, VectorImageData>,
// Vector images for which rasterization at a particular size has started /// Vector images for which rasterization at a particular size has started
// or completed. If completed, the `result` member of `RasterizationTask` /// or completed. If completed, the `result` member of `RasterizationTask`
// contains the rasterized image. /// contains the rasterized image.
rasterized_vector_images: HashMap<(PendingImageId, DeviceIntSize), RasterizationTask>, rasterized_vector_images: HashMap<(PendingImageId, DeviceIntSize), RasterizationTask>,
// The placeholder image used when an image fails to load /// The placeholder image used when an image fails to load
#[conditional_malloc_size_of] #[conditional_malloc_size_of]
placeholder_image: Arc<RasterImage>, placeholder_image: Arc<RasterImage>,
// The URL used for the placeholder image /// The URL used for the placeholder image
placeholder_url: ServoUrl, placeholder_url: ServoUrl,
// Cross-process compositor API instance. /// Cross-process compositor API instance.
#[ignore_malloc_size_of = "Channel from another crate"] #[ignore_malloc_size_of = "Channel from another crate"]
compositor_api: CrossProcessCompositorApi, compositor_api: CrossProcessCompositorApi,
} }
@ -683,39 +682,38 @@ impl ImageCache for ImageCacheImpl {
&self, &self,
pipeline_id: PipelineId, pipeline_id: PipelineId,
image_id: PendingImageId, image_id: PendingImageId,
size: DeviceIntSize, requested_size: DeviceIntSize,
sender: IpcSender<ImageCacheMessage>, sender: IpcSender<ImageCacheResponseMessage>,
) { ) {
let key = (image_id, size);
let completed = { let completed = {
let mut store = self.store.lock().unwrap(); let mut store = self.store.lock().unwrap();
let key = (image_id, requested_size);
if !store.vector_images.contains_key(&image_id) { if !store.vector_images.contains_key(&image_id) {
warn!("Unknown image requested for rasterization for key {key:?}"); warn!("Unknown image requested for rasterization for key {key:?}");
return; return;
}; };
match store.rasterized_vector_images.entry(key) { let Some(task) = store.rasterized_vector_images.get_mut(&key) else {
Occupied(mut entry) => { warn!("Image rasterization task not found in the cache for key {key:?}");
let task = entry.get_mut(); return;
if task.result.is_some() { };
true
} else { match task.result {
task.listeners.push((pipeline_id, sender.clone())); Some(_) => true,
false None => {
} task.listeners.push((pipeline_id, sender.clone()));
}, false
Vacant(_) => {
warn!("Image rasterization task not found in the cache for key {key:?}");
return;
}, },
} }
}; };
if completed { if completed {
let _ = sender.send(ImageCacheMessage::VectorImageRasterizationCompleted( let _ = sender.send(ImageCacheResponseMessage::VectorImageRasterizationComplete(
pipeline_id, RasterizationCompleteResponse {
image_id, pipeline_id,
size, image_id,
requested_size,
},
)); ));
} }
} }
@ -723,7 +721,7 @@ impl ImageCache for ImageCacheImpl {
fn rasterize_vector_image( fn rasterize_vector_image(
&self, &self,
image_id: PendingImageId, image_id: PendingImageId,
size: DeviceIntSize, requested_size: DeviceIntSize,
) -> Option<RasterImage> { ) -> Option<RasterImage> {
let mut store = self.store.lock().unwrap(); let mut store = self.store.lock().unwrap();
let Some(vector_image) = store.vector_images.get(&image_id).cloned() else { let Some(vector_image) = store.vector_images.get(&image_id).cloned() else {
@ -731,44 +729,48 @@ impl ImageCache for ImageCacheImpl {
return None; return None;
}; };
match store.rasterized_vector_images.entry((image_id, size)) { // This early return relies on the fact that the result of image rasterization cannot
Occupied(occupied_entry) => { // ever be `None`. If that were the case we would need to check whether the entry
return occupied_entry.get().result.clone(); // in the `HashMap` was `Occupied` or not.
}, let entry = store
Vacant(entry) => entry.insert(RasterizationTask { .rasterized_vector_images
listeners: vec![], .entry((image_id, requested_size))
result: None, .or_default();
}), if let Some(result) = entry.result.as_ref() {
}; return Some(result.clone());
}
let store = self.store.clone(); let store = self.store.clone();
self.thread_pool.spawn(move || { self.thread_pool.spawn(move || {
let natural_size = vector_image.svg_tree.size().to_int_size(); let natural_size = vector_image.svg_tree.size().to_int_size();
let requested_size = { let tinyskia_requested_size = {
let width = size.width.try_into().unwrap_or(0); let width = requested_size.width.try_into().unwrap_or(0);
let height = size.height.try_into().unwrap_or(0); let height = requested_size.height.try_into().unwrap_or(0);
tiny_skia::IntSize::from_wh(width, height).unwrap_or(natural_size) tiny_skia::IntSize::from_wh(width, height).unwrap_or(natural_size)
}; };
let transform = tiny_skia::Transform::from_scale( let transform = tiny_skia::Transform::from_scale(
requested_size.width() as f32 / natural_size.width() as f32, tinyskia_requested_size.width() as f32 / natural_size.width() as f32,
requested_size.height() as f32 / natural_size.height() as f32, tinyskia_requested_size.height() as f32 / natural_size.height() as f32,
); );
let mut pixmap = let mut pixmap = tiny_skia::Pixmap::new(
tiny_skia::Pixmap::new(requested_size.width(), requested_size.height()).unwrap(); tinyskia_requested_size.width(),
tinyskia_requested_size.height(),
)
.unwrap();
resvg::render(&vector_image.svg_tree, transform, &mut pixmap.as_mut()); resvg::render(&vector_image.svg_tree, transform, &mut pixmap.as_mut());
let bytes = pixmap.take(); let bytes = pixmap.take();
let frame = ImageFrame { let frame = ImageFrame {
delay: None, delay: None,
byte_range: 0..bytes.len(), byte_range: 0..bytes.len(),
width: requested_size.width(), width: tinyskia_requested_size.width(),
height: requested_size.height(), height: tinyskia_requested_size.height(),
}; };
let mut rasterized_image = RasterImage { let mut rasterized_image = RasterImage {
metadata: ImageMetadata { metadata: ImageMetadata {
width: requested_size.width(), width: tinyskia_requested_size.width(),
height: requested_size.height(), height: tinyskia_requested_size.height(),
}, },
format: PixelFormat::RGBA8, format: PixelFormat::RGBA8,
frames: vec![frame], frames: vec![frame],
@ -780,19 +782,23 @@ impl ImageCache for ImageCacheImpl {
let listeners = { let listeners = {
let mut store = store.lock().unwrap(); let mut store = store.lock().unwrap();
set_webrender_image_key(&store.compositor_api, &mut rasterized_image); set_webrender_image_key(&store.compositor_api, &mut rasterized_image);
if let Some(task) = store.rasterized_vector_images.get_mut(&(image_id, size)) { store
task.result = Some(rasterized_image); .rasterized_vector_images
std::mem::take(&mut task.listeners) .get_mut(&(image_id, requested_size))
} else { .map(|task| {
Vec::new() task.result = Some(rasterized_image);
} std::mem::take(&mut task.listeners)
})
.unwrap_or_default()
}; };
for (pipeline_id, sender) in listeners { for (pipeline_id, sender) in listeners {
let _ = sender.send(ImageCacheMessage::VectorImageRasterizationCompleted( let _ = sender.send(ImageCacheResponseMessage::VectorImageRasterizationComplete(
pipeline_id, RasterizationCompleteResponse {
image_id, pipeline_id,
size, image_id,
requested_size,
},
)); ));
} }
}); });

View file

@ -7,6 +7,7 @@ use std::rc::Rc;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::rust::{HandleObject, MutableHandleValue}; use js::rust::{HandleObject, MutableHandleValue};
use net_traits::image_cache::Image;
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::DataTransferBinding::DataTransferMethods; use crate::dom::bindings::codegen::Bindings::DataTransferBinding::DataTransferMethods;
@ -155,10 +156,9 @@ impl DataTransferMethods<crate::DomTypeHolder> for DataTransfer {
// Step 3 // Step 3
if let Some(image) = image.downcast::<HTMLImageElement>() { if let Some(image) = image.downcast::<HTMLImageElement>() {
if let Some(image) = image.image_data().and_then(|image| image.as_raster_image()) { match image.image_data().as_ref().and_then(Image::as_raster_image) {
data_store.set_bitmap(Some(image), x, y); Some(image) => data_store.set_bitmap(Some(image), x, y),
} else { None => warn!("Vector images are not yet supported in setDragImage"),
warn!("Vector images are not yet supported in setDragImage");
} }
} }
} }

View file

@ -341,14 +341,12 @@ impl HTMLImageElement {
is_placeholder, is_placeholder,
}) => { }) => {
if is_placeholder { if is_placeholder {
let image = image if let Some(raster_image) = image.as_raster_image() {
.as_raster_image() self.process_image_response(
.expect("Only raster images are supported as placeholders currently"); ImageResponse::PlaceholderLoaded(raster_image, url),
can_gc,
self.process_image_response( )
ImageResponse::PlaceholderLoaded(image, url), }
can_gc,
)
} else { } else {
self.process_image_response(ImageResponse::Loaded(image, url), can_gc) self.process_image_response(ImageResponse::Loaded(image, url), can_gc)
} }

View file

@ -262,11 +262,10 @@ impl HTMLVideoElement {
match response { match response {
ImageResponse::Loaded(image, url) => { ImageResponse::Loaded(image, url) => {
debug!("Loaded poster image for video element: {:?}", url); debug!("Loaded poster image for video element: {:?}", url);
if let Some(image) = image.as_raster_image() { match image.as_raster_image() {
self.htmlmediaelement.process_poster_image_loaded(image); Some(image) => self.htmlmediaelement.process_poster_image_loaded(image),
} else { None => warn!("Vector images are not yet supported in video poster"),
warn!("Vector images are not yet supported in video poster"); }
};
LoadBlocker::terminate(&self.load_blocker, can_gc); LoadBlocker::terminate(&self.load_blocker, can_gc);
}, },
ImageResponse::MetadataLoaded(..) => {}, ImageResponse::MetadataLoaded(..) => {},

View file

@ -21,8 +21,8 @@ use js::jsval::JSVal;
use js::rust::{HandleObject, MutableHandleValue}; use js::rust::{HandleObject, MutableHandleValue};
use net_traits::http_status::HttpStatus; use net_traits::http_status::HttpStatus;
use net_traits::image_cache::{ use net_traits::image_cache::{
ImageCache, ImageCacheMessage, ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable, ImageCache, ImageCacheResponseMessage, ImageCacheResult, ImageLoadListener,
ImageResponse, PendingImageId, UsePlaceholder, ImageOrMetadataAvailable, ImageResponse, PendingImageId, UsePlaceholder,
}; };
use net_traits::request::{RequestBuilder, RequestId}; use net_traits::request::{RequestBuilder, RequestId};
use net_traits::{ use net_traits::{
@ -929,7 +929,7 @@ impl Notification {
pending_image_id: PendingImageId, pending_image_id: PendingImageId,
resource_type: ResourceType, resource_type: ResourceType,
) { ) {
let (sender, receiver) = ipc::channel::<ImageCacheMessage>().expect("ipc channel failure"); let (sender, receiver) = ipc::channel().expect("ipc channel failure");
let global: &GlobalScope = &self.global(); let global: &GlobalScope = &self.global();
@ -945,7 +945,7 @@ impl Notification {
task_source.queue(task!(handle_response: move || { task_source.queue(task!(handle_response: move || {
let this = trusted_this.root(); let this = trusted_this.root();
if let Ok(response) = response { if let Ok(response) = response {
let ImageCacheMessage::NotifyPendingImageLoadStatus(status) = response else { let ImageCacheResponseMessage::NotifyPendingImageLoadStatus(status) = response else {
warn!("Received unexpected message from image cache: {response:?}"); warn!("Received unexpected message from image cache: {response:?}");
return; return;
}; };

View file

@ -609,23 +609,29 @@ impl WebGLRenderingContext {
}; };
let cors_setting = cors_setting_for_element(image.upcast()); let cors_setting = cors_setting_for_element(image.upcast());
let img = let img = match canvas_utils::request_image_from_cache(
match canvas_utils::request_image_from_cache(&window, img_url, cors_setting) { &window,
ImageResponse::Loaded(image, _) => { img_url,
if let Some(image) = image.as_raster_image() { cors_setting,
image ) {
} else { ImageResponse::Loaded(image, _) => {
// Related https://github.com/KhronosGroup/WebGL/issues/1503? match image.as_raster_image() {
Some(image) => image,
None => {
// Vector images are not currently supported here and there are some open questions
// in the specification about how to handle them:
// See https://github.com/KhronosGroup/WebGL/issues/1503.
warn!( warn!(
"Vector images as are not yet supported as WebGL texture source" "Vector images as are not yet supported as WebGL texture source"
); );
return Ok(None); return Ok(None);
} },
}, }
ImageResponse::PlaceholderLoaded(_, _) | },
ImageResponse::None | ImageResponse::PlaceholderLoaded(_, _) |
ImageResponse::MetadataLoaded(_) => return Ok(None), ImageResponse::None |
}; ImageResponse::MetadataLoaded(_) => return Ok(None),
};
let size = Size2D::new(img.metadata.width, img.metadata.height); let size = Size2D::new(img.metadata.width, img.metadata.height);

View file

@ -55,8 +55,8 @@ use malloc_size_of::MallocSizeOf;
use media::WindowGLContext; use media::WindowGLContext;
use net_traits::ResourceThreads; use net_traits::ResourceThreads;
use net_traits::image_cache::{ use net_traits::image_cache::{
ImageCache, ImageCacheMessage, ImageLoadListener, ImageResponse, PendingImageId, ImageCache, ImageCacheResponseMessage, ImageLoadListener, ImageResponse, PendingImageId,
PendingImageResponse, PendingImageResponse, RasterizationCompleteResponse,
}; };
use net_traits::storage_thread::StorageType; use net_traits::storage_thread::StorageType;
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
@ -243,7 +243,7 @@ pub(crate) struct Window {
#[no_trace] #[no_trace]
image_cache: Arc<dyn ImageCache>, image_cache: Arc<dyn ImageCache>,
#[no_trace] #[no_trace]
image_cache_sender: IpcSender<ImageCacheMessage>, image_cache_sender: IpcSender<ImageCacheResponseMessage>,
window_proxy: MutNullableDom<WindowProxy>, window_proxy: MutNullableDom<WindowProxy>,
document: MutNullableDom<Document>, document: MutNullableDom<Document>,
location: MutNullableDom<Location>, location: MutNullableDom<Location>,
@ -346,13 +346,13 @@ pub(crate) struct Window {
pending_image_callbacks: DomRefCell<HashMap<PendingImageId, Vec<PendingImageCallback>>>, pending_image_callbacks: DomRefCell<HashMap<PendingImageId, Vec<PendingImageCallback>>>,
/// All of the elements that have an outstanding image request that was /// All of the elements that have an outstanding image request that was
/// initiated by layout during a reflow. They are stored in the script thread /// initiated by layout during a reflow. They are stored in the [`ScriptThread`]
/// to ensure that the element can be marked dirty when the image data becomes /// to ensure that the element can be marked dirty when the image data becomes
/// available at some point in the future. /// available at some point in the future.
pending_layout_images: DomRefCell<HashMapTracedValues<PendingImageId, Vec<Dom<Node>>>>, pending_layout_images: DomRefCell<HashMapTracedValues<PendingImageId, Vec<Dom<Node>>>>,
/// Vector images for which layout has intiated rasterization at a specific size /// Vector images for which layout has intiated rasterization at a specific size
/// and whose results are not yet available. They are stored in the script thread /// and whose results are not yet available. They are stored in the [`ScriptThread`]
/// so that the element can be marked dirty once the rasterization is completed. /// so that the element can be marked dirty once the rasterization is completed.
pending_images_for_rasterization: pending_images_for_rasterization:
DomRefCell<HashMapTracedValues<PendingImageRasterizationKey, Vec<Dom<Node>>>>, DomRefCell<HashMapTracedValues<PendingImageRasterizationKey, Vec<Dom<Node>>>>,
@ -577,7 +577,7 @@ impl Window {
&self, &self,
id: PendingImageId, id: PendingImageId,
callback: impl Fn(PendingImageResponse) + 'static, callback: impl Fn(PendingImageResponse) + 'static,
) -> IpcSender<ImageCacheMessage> { ) -> IpcSender<ImageCacheResponseMessage> {
self.pending_image_callbacks self.pending_image_callbacks
.borrow_mut() .borrow_mut()
.entry(id) .entry(id)
@ -608,11 +608,10 @@ impl Window {
pub(crate) fn handle_image_rasterization_complete_notification( pub(crate) fn handle_image_rasterization_complete_notification(
&self, &self,
image_id: PendingImageId, response: RasterizationCompleteResponse,
size: DeviceIntSize,
) { ) {
let mut images = self.pending_images_for_rasterization.borrow_mut(); let mut images = self.pending_images_for_rasterization.borrow_mut();
let nodes = images.entry((image_id, size)); let nodes = images.entry((response.image_id, response.requested_size));
let nodes = match nodes { let nodes = match nodes {
Entry::Occupied(nodes) => nodes, Entry::Occupied(nodes) => nodes,
Entry::Vacant(_) => return, Entry::Vacant(_) => return,
@ -3054,7 +3053,7 @@ impl Window {
script_chan: Sender<MainThreadScriptMsg>, script_chan: Sender<MainThreadScriptMsg>,
layout: Box<dyn Layout>, layout: Box<dyn Layout>,
font_context: Arc<FontContext>, font_context: Arc<FontContext>,
image_cache_sender: IpcSender<ImageCacheMessage>, image_cache_sender: IpcSender<ImageCacheResponseMessage>,
image_cache: Arc<dyn ImageCache>, image_cache: Arc<dyn ImageCache>,
resource_threads: ResourceThreads, resource_threads: ResourceThreads,
#[cfg(feature = "bluetooth")] bluetooth_thread: IpcSender<BluetoothRequest>, #[cfg(feature = "bluetooth")] bluetooth_thread: IpcSender<BluetoothRequest>,

View file

@ -16,7 +16,7 @@ use crossbeam_channel::{Receiver, SendError, Sender, select};
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg}; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg};
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use net_traits::FetchResponseMsg; use net_traits::FetchResponseMsg;
use net_traits::image_cache::ImageCacheMessage; use net_traits::image_cache::ImageCacheResponseMessage;
use profile_traits::mem::{self as profile_mem, OpaqueSender, ReportsChan}; use profile_traits::mem::{self as profile_mem, OpaqueSender, ReportsChan};
use profile_traits::time::{self as profile_time}; use profile_traits::time::{self as profile_time};
use script_traits::{Painter, ScriptThreadMessage}; use script_traits::{Painter, ScriptThreadMessage};
@ -40,7 +40,7 @@ pub(crate) enum MixedMessage {
FromConstellation(ScriptThreadMessage), FromConstellation(ScriptThreadMessage),
FromScript(MainThreadScriptMsg), FromScript(MainThreadScriptMsg),
FromDevtools(DevtoolScriptControlMsg), FromDevtools(DevtoolScriptControlMsg),
FromImageCache(ImageCacheMessage), FromImageCache(ImageCacheResponseMessage),
#[cfg(feature = "webgpu")] #[cfg(feature = "webgpu")]
FromWebGPUServer(WebGPUMsg), FromWebGPUServer(WebGPUMsg),
TimerFired, TimerFired,
@ -105,11 +105,11 @@ impl MixedMessage {
MainThreadScriptMsg::WakeUp => None, MainThreadScriptMsg::WakeUp => None,
}, },
MixedMessage::FromImageCache(response) => match response { MixedMessage::FromImageCache(response) => match response {
ImageCacheMessage::NotifyPendingImageLoadStatus(response) => { ImageCacheResponseMessage::NotifyPendingImageLoadStatus(response) => {
Some(response.pipeline_id) Some(response.pipeline_id)
}, },
ImageCacheMessage::VectorImageRasterizationCompleted(pipeline_id, ..) => { ImageCacheResponseMessage::VectorImageRasterizationComplete(response) => {
Some(*pipeline_id) Some(response.pipeline_id)
}, },
}, },
MixedMessage::FromDevtools(_) | MixedMessage::TimerFired => None, MixedMessage::FromDevtools(_) | MixedMessage::TimerFired => None,
@ -333,7 +333,7 @@ pub(crate) struct ScriptThreadSenders {
/// messages on this channel are routed to crossbeam [`Sender`] on the router thread, which /// messages on this channel are routed to crossbeam [`Sender`] on the router thread, which
/// in turn sends messages to [`ScriptThreadReceivers::image_cache_receiver`]. /// in turn sends messages to [`ScriptThreadReceivers::image_cache_receiver`].
#[no_trace] #[no_trace]
pub(crate) image_cache_sender: IpcSender<ImageCacheMessage>, pub(crate) image_cache_sender: IpcSender<ImageCacheResponseMessage>,
/// For providing contact with the time profiler. /// For providing contact with the time profiler.
#[no_trace] #[no_trace]
@ -362,7 +362,7 @@ pub(crate) struct ScriptThreadReceivers {
/// The [`Receiver`] which receives incoming messages from the `ImageCache`. /// The [`Receiver`] which receives incoming messages from the `ImageCache`.
#[no_trace] #[no_trace]
pub(crate) image_cache_receiver: Receiver<ImageCacheMessage>, pub(crate) image_cache_receiver: Receiver<ImageCacheResponseMessage>,
/// For receiving commands from an optional devtools server. Will be ignored if no such server /// For receiving commands from an optional devtools server. Will be ignored if no such server
/// exists. When devtools are not active this will be [`crossbeam_channel::never()`]. /// exists. When devtools are not active this will be [`crossbeam_channel::never()`].

View file

@ -71,7 +71,7 @@ use js::jsval::UndefinedValue;
use js::rust::ParentRuntime; use js::rust::ParentRuntime;
use media::WindowGLContext; use media::WindowGLContext;
use metrics::MAX_TASK_NS; use metrics::MAX_TASK_NS;
use net_traits::image_cache::{ImageCache, ImageCacheMessage}; use net_traits::image_cache::{ImageCache, ImageCacheResponseMessage};
use net_traits::request::{Referrer, RequestId}; use net_traits::request::{Referrer, RequestId};
use net_traits::response::ResponseInit; use net_traits::response::ResponseInit;
use net_traits::storage_thread::StorageType; use net_traits::storage_thread::StorageType;
@ -2095,9 +2095,9 @@ impl ScriptThread {
} }
} }
fn handle_msg_from_image_cache(&self, response: ImageCacheMessage) { fn handle_msg_from_image_cache(&self, response: ImageCacheResponseMessage) {
match response { match response {
ImageCacheMessage::NotifyPendingImageLoadStatus(pending_image_response) => { ImageCacheResponseMessage::NotifyPendingImageLoadStatus(pending_image_response) => {
let window = self let window = self
.documents .documents
.borrow() .borrow()
@ -2106,10 +2106,10 @@ impl ScriptThread {
window.pending_image_notification(pending_image_response); window.pending_image_notification(pending_image_response);
} }
}, },
ImageCacheMessage::VectorImageRasterizationCompleted(pipeline_id, image_id, size) => { ImageCacheResponseMessage::VectorImageRasterizationComplete(response) => {
let window = self.documents.borrow().find_window(pipeline_id); let window = self.documents.borrow().find_window(response.pipeline_id);
if let Some(ref window) = window { if let Some(ref window) = window {
window.handle_image_rasterization_complete_notification(image_id, size); window.handle_image_rasterization_complete_notification(response);
} }
}, },
}; };

View file

@ -24,9 +24,10 @@ use crate::request::CorsSettings;
// ====================================================================== // ======================================================================
pub type VectorImageId = PendingImageId; pub type VectorImageId = PendingImageId;
// Represents either a raster image for which the pixel data is available // Represents either a raster image for which the pixel data is available
// or a vector image for which only the natural dimensions are available // or a vector image for which only the natural dimensions are available
// and thus require an further rasterization step. // and thus requires a further rasterization step to render.
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub enum Image { pub enum Image {
Raster(#[conditional_malloc_size_of] Arc<RasterImage>), Raster(#[conditional_malloc_size_of] Arc<RasterImage>),
@ -82,12 +83,12 @@ pub enum ImageOrMetadataAvailable {
pub struct ImageLoadListener { pub struct ImageLoadListener {
pipeline_id: PipelineId, pipeline_id: PipelineId,
pub id: PendingImageId, pub id: PendingImageId,
sender: IpcSender<ImageCacheMessage>, sender: IpcSender<ImageCacheResponseMessage>,
} }
impl ImageLoadListener { impl ImageLoadListener {
pub fn new( pub fn new(
sender: IpcSender<ImageCacheMessage>, sender: IpcSender<ImageCacheResponseMessage>,
pipeline_id: PipelineId, pipeline_id: PipelineId,
id: PendingImageId, id: PendingImageId,
) -> ImageLoadListener { ) -> ImageLoadListener {
@ -103,14 +104,15 @@ impl ImageLoadListener {
// This send can fail if thread waiting for this notification has panicked. // This send can fail if thread waiting for this notification has panicked.
// That's not a case that's worth warning about. // That's not a case that's worth warning about.
// TODO(#15501): are there cases in which we should perform cleanup? // TODO(#15501): are there cases in which we should perform cleanup?
let msg = PendingImageResponse {
pipeline_id: self.pipeline_id,
response,
id: self.id,
};
let _ = self let _ = self
.sender .sender
.send(ImageCacheMessage::NotifyPendingImageLoadStatus(msg)); .send(ImageCacheResponseMessage::NotifyPendingImageLoadStatus(
PendingImageResponse {
pipeline_id: self.pipeline_id,
response,
id: self.id,
},
));
} }
} }
@ -139,9 +141,16 @@ pub struct PendingImageResponse {
} }
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub enum ImageCacheMessage { pub struct RasterizationCompleteResponse {
pub pipeline_id: PipelineId,
pub image_id: PendingImageId,
pub requested_size: DeviceIntSize,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum ImageCacheResponseMessage {
NotifyPendingImageLoadStatus(PendingImageResponse), NotifyPendingImageLoadStatus(PendingImageResponse),
VectorImageRasterizationCompleted(PipelineId, PendingImageId, DeviceIntSize), VectorImageRasterizationComplete(RasterizationCompleteResponse),
} }
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
@ -184,26 +193,26 @@ pub trait ImageCache: Sync + Send {
use_placeholder: UsePlaceholder, use_placeholder: UsePlaceholder,
) -> ImageCacheResult; ) -> ImageCacheResult;
// Returns `Some` if the given `image_id` has already been rasterized at the given `size`. /// Returns `Some` if the given `image_id` has already been rasterized at the given `size`.
// Otherwise, triggers a new job to perform the rasterization. If a notification /// Otherwise, triggers a new job to perform the rasterization. If a notification
// is needed after rasterization is completed, the `add_rasterization_complete_listener` /// is needed after rasterization is completed, the `add_rasterization_complete_listener`
// API below can be used to add a listener. /// API below can be used to add a listener.
fn rasterize_vector_image( fn rasterize_vector_image(
&self, &self,
image_id: VectorImageId, image_id: VectorImageId,
size: DeviceIntSize, size: DeviceIntSize,
) -> Option<RasterImage>; ) -> Option<RasterImage>;
// Adds a new listener to be notified once the given `image_id` has been rasterized at /// Adds a new listener to be notified once the given `image_id` has been rasterized at
// the given `size`. The listener will receive a `VectorImageRasterizationCompleted` /// the given `size`. The listener will receive a `VectorImageRasterizationComplete`
// message on the given `sender`, even if the listener is called after rasterization /// message on the given `sender`, even if the listener is called after rasterization
// at has already completed. /// at has already completed.
fn add_rasterization_complete_listener( fn add_rasterization_complete_listener(
&self, &self,
pipeline_id: PipelineId, pipeline_id: PipelineId,
image_id: VectorImageId, image_id: VectorImageId,
size: DeviceIntSize, size: DeviceIntSize,
sender: IpcSender<ImageCacheMessage>, sender: IpcSender<ImageCacheResponseMessage>,
); );
/// Add a new listener for the given pending image id. If the image is already present, /// Add a new listener for the given pending image id. If the image is already present,

View file

@ -154,8 +154,9 @@ pub struct PendingImage {
pub origin: ImmutableOrigin, pub origin: ImmutableOrigin,
} }
// A vector image that is fully loaded (i.e has a parsed SVG tree) but not yet /// A data structure to tarck vector image that are fully loaded (i.e has a parsed SVG
// rasterized to the size needed by layout. /// tree) but not yet rasterized to the size needed by layout. The rasterization is
/// happening in the image cache.
#[derive(Debug)] #[derive(Debug)]
pub struct PendingRasterizationImage { pub struct PendingRasterizationImage {
pub node: UntrustedNodeAddress, pub node: UntrustedNodeAddress,