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