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