Skip to main content

net_traits/
image_cache.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::sync::Arc;
6
7use log::debug;
8use malloc_size_of::MallocSizeOfOps;
9use malloc_size_of_derive::MallocSizeOf;
10use paint_api::CrossProcessPaintApi;
11use pixels::{CorsStatus, ImageMetadata, RasterImage};
12use profile_traits::mem::Report;
13use resvg::usvg::{Font, fontdb};
14use serde::{Deserialize, Serialize};
15use servo_base::id::{PipelineId, WebViewId};
16use servo_url::{ImmutableOrigin, ServoUrl};
17use uuid::Uuid;
18use webrender_api::ImageKey;
19use webrender_api::units::DeviceIntSize;
20
21use crate::FetchResponseMsg;
22use crate::request::CorsSettings;
23
24// ======================================================================
25// Aux structs and enums.
26// ======================================================================
27
28/// An interface for resolving font families and styles for SVG images.
29pub trait FontResolver: Sync + Send {
30    /// Attempt to resolve a font reference using the provided database of fonts.
31    /// Adding new fonts to the database is allowed. Return an index into the database
32    /// if the font resolves to an entry, otherwise return None.
33    fn resolve(&self, font: &Font, database: &mut Arc<fontdb::Database>) -> Option<fontdb::ID>;
34}
35
36pub type VectorImageId = PendingImageId;
37
38// Represents either a raster image for which the pixel data is available
39// or a vector image for which only the natural dimensions are available
40// and thus requires a further rasterization step to render.
41#[derive(Clone, Debug, MallocSizeOf)]
42pub enum Image {
43    Raster(#[conditional_malloc_size_of] Arc<RasterImage>),
44    Vector(VectorImage),
45}
46
47#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
48pub struct VectorImage {
49    pub id: VectorImageId,
50    pub svg_id: Option<Uuid>,
51    pub metadata: ImageMetadata,
52    pub cors_status: CorsStatus,
53}
54
55impl Image {
56    pub fn metadata(&self) -> ImageMetadata {
57        match self {
58            Image::Vector(image, ..) => image.metadata,
59            Image::Raster(image) => image.metadata,
60        }
61    }
62
63    pub fn cors_status(&self) -> CorsStatus {
64        match self {
65            Image::Vector(image) => image.cors_status,
66            Image::Raster(image) => image.cors_status,
67        }
68    }
69
70    pub fn as_raster_image(&self) -> Option<Arc<RasterImage>> {
71        match self {
72            Image::Raster(image) => Some(image.clone()),
73            Image::Vector(..) => None,
74        }
75    }
76}
77
78/// Indicating either entire image or just metadata availability
79#[derive(Clone, Debug, MallocSizeOf)]
80pub enum ImageOrMetadataAvailable {
81    ImageAvailable { image: Image, url: ServoUrl },
82    MetadataAvailable(ImageMetadata, PendingImageId),
83}
84
85pub type ImageCacheResponseCallback = Box<dyn Fn(ImageCacheResponseMessage) + Send + 'static>;
86
87/// This is optionally passed to the image cache when requesting
88/// and image, and returned to the specified event loop when the
89/// image load completes. It is typically used to trigger a reflow
90/// and/or repaint.
91#[derive(MallocSizeOf)]
92pub struct ImageLoadListener {
93    pipeline_id: PipelineId,
94    pub id: PendingImageId,
95    #[ignore_malloc_size_of = "Difficult to measure FnOnce"]
96    callback: ImageCacheResponseCallback,
97}
98
99impl ImageLoadListener {
100    pub fn new(
101        callback: ImageCacheResponseCallback,
102        pipeline_id: PipelineId,
103        id: PendingImageId,
104    ) -> ImageLoadListener {
105        ImageLoadListener {
106            pipeline_id,
107            callback,
108            id,
109        }
110    }
111
112    pub fn respond(&self, response: ImageResponse) {
113        debug!("Notifying listener");
114        (self.callback)(ImageCacheResponseMessage::NotifyPendingImageLoadStatus(
115            PendingImageResponse {
116                pipeline_id: self.pipeline_id,
117                response,
118                id: self.id,
119            },
120        ));
121    }
122}
123
124/// The returned image.
125#[derive(Clone, Debug, MallocSizeOf)]
126pub enum ImageResponse {
127    /// The requested image was loaded.
128    Loaded(Image, ServoUrl),
129    /// The request image metadata was loaded.
130    MetadataLoaded(ImageMetadata),
131    /// The requested image failed to load or decode.
132    FailedToLoadOrDecode,
133}
134
135/// The unique id for an image that has previously been requested.
136#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
137pub struct PendingImageId(pub u64);
138
139#[derive(Clone, Debug)]
140pub struct PendingImageResponse {
141    pub pipeline_id: PipelineId,
142    pub response: ImageResponse,
143    pub id: PendingImageId,
144}
145
146#[derive(Clone, Debug, Deserialize, Serialize)]
147pub struct RasterizationCompleteResponse {
148    pub pipeline_id: PipelineId,
149    pub image_id: PendingImageId,
150    pub requested_size: DeviceIntSize,
151}
152
153#[derive(Clone, Debug)]
154pub enum ImageCacheResponseMessage {
155    NotifyPendingImageLoadStatus(PendingImageResponse),
156    VectorImageRasterizationComplete(RasterizationCompleteResponse),
157}
158
159// ======================================================================
160// ImageCache public API.
161// ======================================================================
162
163pub enum ImageCacheResult {
164    Available(ImageOrMetadataAvailable),
165    FailedToLoadOrDecode,
166    Pending(PendingImageId),
167    ReadyForRequest(PendingImageId),
168}
169
170/// A shared [`ImageCacheFactory`] is a per-process data structure used to create an [`ImageCache`]
171/// inside that process in any `ScriptThread`. This allows sharing the same font database (for
172/// SVGs) and also decoding thread pool among all [`ImageCache`]s in the same process.
173pub trait ImageCacheFactory: Sync + Send {
174    fn create(
175        &self,
176        webview_id: WebViewId,
177        pipeline_id: PipelineId,
178        paint_api: &CrossProcessPaintApi,
179        font_resolver: Arc<dyn FontResolver>,
180    ) -> Arc<dyn ImageCache>;
181}
182
183/// An [`ImageCache`] manages fetching and decoding images for a single `Pipeline` for its
184/// `Document` and all of its associated `Worker`s.
185pub trait ImageCache: Sync + Send {
186    fn memory_reports(&self, prefix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report>;
187
188    #[cfg(feature = "test-util")]
189    /// Returns the number of rasterization tasks
190    fn number_of_rasterize_tasks(&self) -> usize;
191
192    /// Get an [`ImageKey`] to be used for external WebRender image management for
193    /// things like canvas rendering. Returns `None` when an [`ImageKey`] cannot
194    /// be generated properly.
195    fn get_image_key(&self) -> Option<ImageKey>;
196
197    /// Definitively check whether there is a cached, fully loaded image available.
198    fn get_image(
199        &self,
200        url: ServoUrl,
201        origin: ImmutableOrigin,
202        cors_setting: Option<CorsSettings>,
203    ) -> Option<Image>;
204
205    /// Returns if the Image is already in the cache or not. If the Image is not yet completely decoded, we return [`ImageCacheResult::Pending`] or [`ImageCacheResult::Available`].
206    fn get_cached_image_status(
207        &self,
208        url: ServoUrl,
209        origin: ImmutableOrigin,
210        cors_setting: Option<CorsSettings>,
211    ) -> ImageCacheResult;
212
213    /// Returns `Some` if the given `image_id` has already been rasterized at the given `size`.
214    /// Otherwise, triggers a new job to perform the rasterization. If a notification
215    /// is needed after rasterization is completed, the `add_rasterization_complete_listener`
216    /// API below can be used to add a listener.
217    fn rasterize_vector_image(
218        &self,
219        image_id: VectorImageId,
220        size: DeviceIntSize,
221        svg_id: Option<Uuid>,
222    ) -> Option<RasterImage>;
223
224    /// Adds a new listener to be notified once the given `image_id` has been rasterized at
225    /// the given `size`. The listener will receive a `VectorImageRasterizationComplete`
226    /// message on the given `sender`, even if the listener is called after rasterization
227    /// at has already completed.
228    fn add_rasterization_complete_listener(
229        &self,
230        pipeline_id: PipelineId,
231        image_id: VectorImageId,
232        size: DeviceIntSize,
233        callback: ImageCacheResponseCallback,
234    );
235
236    /// Removes the rasterized image from the image_cache, identified by the id of the SVG
237    fn evict_rasterized_image(&self, svg_id: &Uuid);
238
239    /// Removes the completed image from the image_cache, identified by url, origin, and cors
240    fn evict_completed_image(
241        &self,
242        url: &ServoUrl,
243        origin: &ImmutableOrigin,
244        cors_setting: &Option<CorsSettings>,
245    );
246
247    /// Synchronously get the broken image icon for this [`ImageCache`]. This will
248    /// allocate space for this icon and upload it to WebRender.
249    fn get_broken_image_icon(&self) -> Option<Arc<RasterImage>>;
250
251    /// Add a new listener for the given pending image id. If the image is already present,
252    /// the responder will still receive the expected response.
253    fn add_listener(&self, listener: ImageLoadListener);
254
255    /// Inform the image cache about a response for a pending request.
256    fn notify_pending_response(&self, id: PendingImageId, action: FetchResponseMsg);
257
258    /// Fills the image cache with a batch of keys.
259    fn dispatch_fill_key_cache_with_batch_of_keys(&self, image_keys: Vec<ImageKey>);
260
261    /// Clear the image cache.
262    fn clear(&self);
263}