Skip to main content

net/
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::cell::OnceCell;
6use std::cmp::min;
7use std::collections::hash_map::Entry::{Occupied, Vacant};
8use std::collections::{HashMap, HashSet, VecDeque};
9use std::mem;
10use std::sync::Arc;
11
12use imsz::imsz_from_reader;
13use log::{debug, warn};
14use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOf as MallocSizeOfTrait, MallocSizeOfOps};
15use malloc_size_of_derive::MallocSizeOf;
16use mime::Mime;
17use net_traits::image_cache::{
18    Image, ImageCache, ImageCacheFactory, ImageCacheResponseCallback, ImageCacheResponseMessage,
19    ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable, ImageResponse, PendingImageId,
20    RasterizationCompleteResponse, VectorImage,
21};
22use net_traits::request::CorsSettings;
23use net_traits::{FetchMetadata, FetchResponseMsg, FilteredMetadata, NetworkError};
24use paint_api::{CrossProcessPaintApi, ImageUpdate, SerializableImageData};
25use parking_lot::Mutex;
26use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage, load_from_memory};
27use profile_traits::mem::{Report, ReportKind};
28use profile_traits::path;
29use resvg::tiny_skia;
30use resvg::usvg::{self, fontdb};
31use rustc_hash::{FxHashMap, FxHashSet};
32use servo_base::id::{PipelineId, WebViewId};
33use servo_base::threadpool::ThreadPool;
34use servo_url::{ImmutableOrigin, ServoUrl};
35use webrender_api::ImageKey as WebRenderImageKey;
36use webrender_api::units::DeviceIntSize;
37
38// We bake in rippy.png as a fallback, in case the embedder does not provide a broken
39// image icon resource. This version is 229 bytes, so don't exchange it against
40// something of higher resolution.
41const FALLBACK_RIPPY: &[u8] = include_bytes!("resources/rippy.png");
42
43/// The current SVG stack relies on `resvg` to provide the natural dimensions of
44/// the SVG, which it automatically infers from the width/height/viewBox properties
45/// of the SVG. Since these can be arbitrarily large, this can cause us to allocate
46/// a pixmap with very large dimensions leading to the process being killed due to
47/// memory exhaustion. For example, the `/css/css-transforms/perspective-svg-001.html`
48/// test uses very large values for viewBox. Hence, we just clamp the maximum
49/// width/height of the pixmap allocated for rasterization.
50const MAX_SVG_PIXMAP_DIMENSION: u32 = 5000;
51
52//
53// TODO(gw): Remaining work on image cache:
54//     * Make use of the prefetch support in various parts of the code.
55//     * Profile time in GetImageIfAvailable - might be worth caching these
56//       results per paint / layout.
57//
58// MAYBE(Yoric):
59//     * For faster lookups, it might be useful to store the LoadKey in the
60//       DOM once we have performed a first load.
61
62// ======================================================================
63// Helper functions.
64// ======================================================================
65
66fn parse_svg_document_in_memory(
67    bytes: &[u8],
68    fontdb: Arc<fontdb::Database>,
69) -> Result<usvg::Tree, &'static str> {
70    let image_string_href_resolver = Box::new(move |_: &str, _: &usvg::Options| {
71        // Do not try to load `href` in <image> as local file path.
72        None
73    });
74
75    let opt = usvg::Options {
76        image_href_resolver: usvg::ImageHrefResolver {
77            resolve_data: usvg::ImageHrefResolver::default_data_resolver(),
78            resolve_string: image_string_href_resolver,
79        },
80        fontdb,
81        ..usvg::Options::default()
82    };
83
84    usvg::Tree::from_data(bytes, &opt)
85        .inspect_err(|error| {
86            warn!("Error when parsing SVG data: {error}");
87        })
88        .map_err(|_| "Not a valid SVG document")
89}
90
91fn decode_bytes_sync(
92    key: LoadKey,
93    bytes: &[u8],
94    cors: CorsStatus,
95    content_type: Option<Mime>,
96    fontdb: Arc<fontdb::Database>,
97) -> DecoderMsg {
98    let is_svg_document = content_type.is_some_and(|content_type| {
99        (
100            content_type.type_(),
101            content_type.subtype(),
102            content_type.suffix(),
103        ) == (mime::IMAGE, mime::SVG, Some(mime::XML))
104    });
105
106    let image = if is_svg_document {
107        parse_svg_document_in_memory(bytes, fontdb)
108            .ok()
109            .map(|svg_tree| {
110                DecodedImage::Vector(VectorImageData {
111                    svg_tree: Arc::new(svg_tree),
112                    cors_status: cors,
113                })
114            })
115    } else {
116        load_from_memory(bytes, cors).map(DecodedImage::Raster)
117    };
118
119    DecoderMsg { key, image }
120}
121
122fn set_webrender_image_key(
123    paint_api: &CrossProcessPaintApi,
124    image: &mut RasterImage,
125    image_key: WebRenderImageKey,
126) {
127    if image.id.is_some() {
128        return;
129    }
130
131    let (descriptor, ipc_shared_memory, should_be_cached) =
132        image.webrender_image_descriptor_and_data_for_frame(0);
133    let data = SerializableImageData::Raw(ipc_shared_memory);
134
135    paint_api.add_image(image_key, descriptor, data, should_be_cached);
136    image.id = Some(image_key);
137}
138
139// ======================================================================
140// Aux structs and enums.
141// ======================================================================
142
143/// <https://html.spec.whatwg.org/multipage/#list-of-available-images>
144type ImageKey = (ServoUrl, ImmutableOrigin, Option<CorsSettings>);
145
146// Represents all the currently pending loads/decodings. For
147// performance reasons, loads are indexed by a dedicated load key.
148#[derive(MallocSizeOf)]
149struct AllPendingLoads {
150    // The loads, indexed by a load key. Used during most operations,
151    // for performance reasons.
152    loads: FxHashMap<LoadKey, PendingLoad>,
153
154    // Get a load key from its url and requesting origin. Used ony when starting and
155    // finishing a load or when adding a new listener.
156    url_to_load_key: HashMap<ImageKey, LoadKey>,
157
158    // A counter used to generate instances of LoadKey
159    keygen: LoadKeyGenerator,
160}
161
162impl AllPendingLoads {
163    fn new() -> AllPendingLoads {
164        AllPendingLoads {
165            loads: FxHashMap::default(),
166            url_to_load_key: HashMap::default(),
167            keygen: LoadKeyGenerator::new(),
168        }
169    }
170
171    // get a PendingLoad from its LoadKey.
172    fn get_by_key_mut(&mut self, key: &LoadKey) -> Option<&mut PendingLoad> {
173        self.loads.get_mut(key)
174    }
175
176    fn remove(&mut self, key: &LoadKey) -> Option<PendingLoad> {
177        self.loads.remove(key).inspect(|pending_load| {
178            self.url_to_load_key
179                .remove(&(
180                    pending_load.url.clone(),
181                    pending_load.load_origin.clone(),
182                    pending_load.cors_setting,
183                ))
184                .unwrap();
185        })
186    }
187
188    fn get_cached(
189        &mut self,
190        url: ServoUrl,
191        origin: ImmutableOrigin,
192        cors_status: Option<CorsSettings>,
193    ) -> CacheResult<'_> {
194        match self
195            .url_to_load_key
196            .entry((url.clone(), origin.clone(), cors_status))
197        {
198            Occupied(url_entry) => {
199                let load_key = url_entry.get();
200                CacheResult::Hit(*load_key, self.loads.get_mut(load_key).unwrap())
201            },
202            Vacant(url_entry) => {
203                let load_key = self.keygen.next();
204                url_entry.insert(load_key);
205
206                let pending_load = PendingLoad::new(url, origin, cors_status);
207                match self.loads.entry(load_key) {
208                    Occupied(_) => unreachable!(),
209                    Vacant(load_entry) => {
210                        let mut_load = load_entry.insert(pending_load);
211                        CacheResult::Miss(Some((load_key, mut_load)))
212                    },
213                }
214            },
215        }
216    }
217}
218
219/// Result of accessing a cache.
220enum CacheResult<'a> {
221    /// The value was in the cache.
222    Hit(LoadKey, &'a mut PendingLoad),
223    /// The value was not in the cache and needed to be regenerated.
224    Miss(Option<(LoadKey, &'a mut PendingLoad)>),
225}
226
227/// Represents an image that has completed loading.
228/// Images that fail to load (due to network or decode
229/// failure) are still stored here, so that they aren't
230/// fetched again.
231#[derive(MallocSizeOf)]
232struct CompletedLoad {
233    image_response: ImageResponse,
234    id: PendingImageId,
235}
236
237impl CompletedLoad {
238    fn new(image_response: ImageResponse, id: PendingImageId) -> CompletedLoad {
239        CompletedLoad { image_response, id }
240    }
241}
242
243#[derive(Clone, MallocSizeOf)]
244struct VectorImageData {
245    #[conditional_malloc_size_of]
246    svg_tree: Arc<usvg::Tree>,
247    cors_status: CorsStatus,
248}
249
250impl std::fmt::Debug for VectorImageData {
251    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252        f.debug_struct("VectorImageData").finish()
253    }
254}
255
256enum DecodedImage {
257    Raster(RasterImage),
258    Vector(VectorImageData),
259}
260
261/// Message that the decoder worker threads send to the image cache.
262struct DecoderMsg {
263    key: LoadKey,
264    image: Option<DecodedImage>,
265}
266
267#[derive(MallocSizeOf)]
268enum ImageBytes {
269    InProgress(Vec<u8>),
270    Complete(#[conditional_malloc_size_of] Arc<Vec<u8>>),
271}
272
273impl ImageBytes {
274    fn extend_from_slice(&mut self, data: &[u8]) {
275        match *self {
276            ImageBytes::InProgress(ref mut bytes) => bytes.extend_from_slice(data),
277            ImageBytes::Complete(_) => panic!("attempted modification of complete image bytes"),
278        }
279    }
280
281    fn mark_complete(&mut self) -> Arc<Vec<u8>> {
282        let bytes = {
283            let own_bytes = match *self {
284                ImageBytes::InProgress(ref mut bytes) => bytes,
285                ImageBytes::Complete(_) => panic!("attempted modification of complete image bytes"),
286            };
287            mem::take(own_bytes)
288        };
289        let bytes = Arc::new(bytes);
290        *self = ImageBytes::Complete(bytes.clone());
291        bytes
292    }
293
294    fn as_slice(&self) -> &[u8] {
295        match *self {
296            ImageBytes::InProgress(ref bytes) => bytes,
297            ImageBytes::Complete(ref bytes) => bytes,
298        }
299    }
300}
301
302// A key used to communicate during loading.
303type LoadKey = PendingImageId;
304
305#[derive(MallocSizeOf)]
306struct LoadKeyGenerator {
307    counter: u64,
308}
309
310impl LoadKeyGenerator {
311    fn new() -> LoadKeyGenerator {
312        LoadKeyGenerator { counter: 0 }
313    }
314    fn next(&mut self) -> PendingImageId {
315        self.counter += 1;
316        PendingImageId(self.counter)
317    }
318}
319
320#[derive(Debug)]
321enum LoadResult {
322    LoadedRasterImage(RasterImage),
323    LoadedVectorImage(VectorImageData),
324    FailedToLoadOrDecode,
325}
326
327/// Represents an image that is either being loaded
328/// by the resource thread, or decoded by a worker thread.
329#[derive(MallocSizeOf)]
330struct PendingLoad {
331    /// The bytes loaded so far. Reset to an empty vector once loading
332    /// is complete and the buffer has been transmitted to the decoder.
333    bytes: ImageBytes,
334
335    /// Image metadata, if available.
336    metadata: Option<ImageMetadata>,
337
338    /// Once loading is complete, the result of the operation.
339    result: Option<Result<(), NetworkError>>,
340
341    /// The listeners that are waiting for this response to complete.
342    listeners: Vec<ImageLoadListener>,
343
344    /// The url being loaded. Do not forget that this may be several Mb
345    /// if we are loading a data: url.
346    url: ServoUrl,
347
348    /// The origin that requested this load.
349    load_origin: ImmutableOrigin,
350
351    /// The CORS attribute setting for the requesting
352    cors_setting: Option<CorsSettings>,
353
354    /// The CORS status of this image response.
355    cors_status: CorsStatus,
356
357    /// The URL of the final response that contains a body.
358    final_url: Option<ServoUrl>,
359
360    /// The MIME type from the `Content-type` header of the HTTP response, if any.
361    content_type: Option<Mime>,
362}
363
364impl PendingLoad {
365    fn new(
366        url: ServoUrl,
367        load_origin: ImmutableOrigin,
368        cors_setting: Option<CorsSettings>,
369    ) -> PendingLoad {
370        PendingLoad {
371            bytes: ImageBytes::InProgress(vec![]),
372            metadata: None,
373            result: None,
374            listeners: vec![],
375            url,
376            load_origin,
377            final_url: None,
378            cors_setting,
379            cors_status: CorsStatus::Unsafe,
380            content_type: None,
381        }
382    }
383
384    fn add_listener(&mut self, listener: ImageLoadListener) {
385        self.listeners.push(listener);
386    }
387}
388
389#[derive(Default, MallocSizeOf)]
390struct RasterizationTask {
391    #[ignore_malloc_size_of = "Fn is difficult to measure"]
392    listeners: Vec<(PipelineId, ImageCacheResponseCallback)>,
393    result: Option<RasterImage>,
394}
395
396/// Used for storing images that do not have a `WebRenderImageKey` yet.
397#[derive(Debug, MallocSizeOf)]
398enum PendingKey {
399    RasterImage((LoadKey, RasterImage)),
400    Svg((LoadKey, RasterImage, DeviceIntSize)),
401}
402
403/// The state of the `WebRenderImageKey`` cache
404#[derive(Debug, MallocSizeOf)]
405enum KeyCacheState {
406    /// We already requested a batch of keys.
407    PendingBatch,
408    /// We have some keys in the cache.
409    Ready(Vec<WebRenderImageKey>),
410    /// Currently filling images from the KeyCache. No new keys will be requested.
411    Processing,
412}
413
414impl KeyCacheState {
415    fn size(&self) -> usize {
416        match self {
417            KeyCacheState::PendingBatch | KeyCacheState::Processing => 0,
418            KeyCacheState::Ready(items) => items.len(),
419        }
420    }
421}
422
423/// As getting new keys takes a round trip over the constellation, we keep a small cache of them.
424/// Additionally, this cache will store image resources that do not have a key yet because those
425/// are needed to complete the load.
426#[derive(MallocSizeOf)]
427struct KeyCache {
428    /// A cache of `WebRenderImageKey`.
429    cache: KeyCacheState,
430    /// These images are loaded but have no key assigned to yet.
431    images_pending_keys: VecDeque<PendingKey>,
432    /// A set of `LoadKey` and image size pairs which have been evicted
433    /// but are either being rasterized or are in images_pending_key
434    evicted_images: HashSet<(LoadKey, DeviceIntSize)>,
435}
436
437impl KeyCache {
438    fn new() -> Self {
439        KeyCache {
440            cache: KeyCacheState::Ready(Vec::new()),
441            images_pending_keys: VecDeque::new(),
442            evicted_images: HashSet::new(),
443        }
444    }
445}
446
447#[derive(Debug, Default, MallocSizeOf)]
448/// A structure that stores if a current SVG element with a PendingImageId and a DeviceIntSize is already being rasterized.
449struct SvgRasterizationTaskStore(FxHashSet<(PendingImageId, DeviceIntSize)>);
450
451impl SvgRasterizationTaskStore {
452    /// Returns true if it is already being rasterized, otherwise false and sets it.
453    fn is_or_set_being_rasterized(
454        &mut self,
455        pending_image_id: PendingImageId,
456        size: DeviceIntSize,
457    ) -> bool {
458        !self.0.insert((pending_image_id, size))
459    }
460
461    /// Removes the task
462    fn remove_being_rasterized(&mut self, pending_image_id: PendingImageId, size: DeviceIntSize) {
463        self.0.remove(&(pending_image_id, size));
464    }
465
466    fn remove_all_for_id(&mut self, pending_image_id: PendingImageId) {
467        self.0.retain(|(id, _size)| *id != pending_image_id);
468    }
469}
470
471/// ## Image cache implementation.
472#[derive(MallocSizeOf)]
473struct ImageCacheStore {
474    /// Images that are loading over network, or decoding.
475    pending_loads: AllPendingLoads,
476
477    /// Images that have finished loading (successful or not)
478    completed_loads: HashMap<ImageKey, CompletedLoad>,
479
480    /// Vector (e.g. SVG) images that have been sucessfully loaded and parsed
481    /// but are yet to be rasterized. Since the same SVG data can be used for
482    /// rasterizing at different sizes, we use this hasmap to share the data.
483    vector_images: FxHashMap<PendingImageId, VectorImageData>,
484
485    /// Vector images for which rasterization at a particular size has started
486    /// or completed. If completed, the `result` member of `RasterizationTask`
487    /// contains the rasterized image.
488    rasterized_vector_images: FxHashMap<(PendingImageId, DeviceIntSize), RasterizationTask>,
489
490    /// Maps a pending image id to a set of sizes for which that image was requested
491    svg_rasterization_task_store: SvgRasterizationTaskStore,
492
493    /// The [`RasterImage`] used for the broken image icon, initialized lazily, only when necessary.
494    #[conditional_malloc_size_of]
495    broken_image_icon_image: OnceCell<Option<Arc<RasterImage>>>,
496
497    /// Cross-process `Paint` API instance.
498    paint_api: CrossProcessPaintApi,
499
500    /// The [`WebView`] of the `Webview` associated with this [`ImageCache`].
501    webview_id: WebViewId,
502
503    /// The [`PipelineId`] of the `Pipeline` associated with this [`ImageCache`].
504    pipeline_id: PipelineId,
505
506    /// Main struct to handle the cache of `WebRenderImageKey` and
507    /// images that do not have a key yet.
508    key_cache: KeyCache,
509}
510
511impl ImageCacheStore {
512    #[cfg(feature = "test-util")]
513    fn number_of_rasterize_tasks(&self) -> usize {
514        self.svg_rasterization_task_store.0.len()
515    }
516
517    /// Finishes loading the image by setting the WebRenderImageKey and calling `compete_load` or `complete_load_svg`.
518    fn set_key_and_finish_load(&mut self, pending_image: PendingKey, image_key: WebRenderImageKey) {
519        match pending_image {
520            PendingKey::RasterImage((pending_id, mut raster_image)) => {
521                // We can have concurrent sync and async loads for the same image, so if it's
522                // not pending anymore we early return since the async result will be ignored in that case.
523                if self.pending_loads.get_by_key_mut(&pending_id).is_none() {
524                    return;
525                }
526                set_webrender_image_key(&self.paint_api, &mut raster_image, image_key);
527                self.complete_load(pending_id, LoadResult::LoadedRasterImage(raster_image));
528            },
529            PendingKey::Svg((pending_id, mut raster_image, requested_size)) => {
530                // We can have concurrent sync and async loads for the same image, so if it's
531                // not pending anymore we early return since the async result will be ignored in that case.
532                if !self
533                    .rasterized_vector_images
534                    .contains_key(&(pending_id, requested_size))
535                {
536                    return;
537                }
538                set_webrender_image_key(&self.paint_api, &mut raster_image, image_key);
539                self.svg_rasterization_task_store
540                    .remove_being_rasterized(pending_id, requested_size);
541                self.complete_load_svg(raster_image, pending_id, requested_size);
542            },
543        }
544    }
545
546    /// If a key is available the image will be immediately loaded, otherwise it will load then the next batch of
547    /// keys is received. Only call this if the image does not have a `LoadKey` yet.
548    fn load_image_with_keycache(&mut self, pending_image: PendingKey) {
549        if let PendingKey::Svg((pending_id, ref _raster_image, requested_size)) = pending_image &&
550            self.key_cache
551                .evicted_images
552                .remove(&(pending_id, requested_size))
553        {
554            self.svg_rasterization_task_store
555                .remove_being_rasterized(pending_id, requested_size);
556            return;
557        }
558        match self.key_cache.cache {
559            KeyCacheState::PendingBatch | KeyCacheState::Processing => {
560                self.key_cache.images_pending_keys.push_back(pending_image);
561            },
562            KeyCacheState::Ready(ref mut cache) => match cache.pop() {
563                Some(image_key) => {
564                    self.set_key_and_finish_load(pending_image, image_key);
565                },
566                None => {
567                    self.key_cache.images_pending_keys.push_back(pending_image);
568                    self.fetch_more_image_keys();
569                },
570            },
571        }
572    }
573
574    fn evict_image_from_keycache(
575        &mut self,
576        image_id: &PendingImageId,
577        requested_size: &DeviceIntSize,
578    ) {
579        self.key_cache
580            .evicted_images
581            .insert((*image_id, *requested_size));
582    }
583
584    fn fetch_more_image_keys(&mut self) {
585        self.key_cache.cache = KeyCacheState::PendingBatch;
586        self.paint_api
587            .generate_image_key_async(self.webview_id, self.pipeline_id);
588    }
589
590    /// Insert received keys into the cache and complete the loading of images.
591    fn insert_keys_and_load_images(&mut self, image_keys: Vec<WebRenderImageKey>) {
592        if let KeyCacheState::Processing = self.key_cache.cache {
593            // We can set this now to ready as we have the exclusive write access.
594            self.key_cache.cache = KeyCacheState::Ready(image_keys);
595            let len = min(
596                self.key_cache.cache.size(),
597                self.key_cache.images_pending_keys.len(),
598            );
599            let images = self
600                .key_cache
601                .images_pending_keys
602                .drain(0..len)
603                .collect::<Vec<PendingKey>>();
604            for key in images {
605                self.load_image_with_keycache(key);
606            }
607            // It is important to fetch new image keys as we might have missed previous returns.
608            if !self.key_cache.images_pending_keys.is_empty() {
609                self.paint_api
610                    .generate_image_key_async(self.webview_id, self.pipeline_id);
611                self.key_cache.cache = KeyCacheState::PendingBatch
612            }
613        } else {
614            unreachable!("A batch was received while we didn't request one")
615        }
616    }
617
618    /// Complete the loading the of the rasterized svg image. This needs the `RasterImage` to
619    /// already have a `WebRenderImageKey`.
620    fn complete_load_svg(
621        &mut self,
622        rasterized_image: RasterImage,
623        pending_image_id: PendingImageId,
624        requested_size: DeviceIntSize,
625    ) {
626        let listeners = {
627            self.rasterized_vector_images
628                .get_mut(&(pending_image_id, requested_size))
629                .map(|task| {
630                    task.result = Some(rasterized_image);
631                    std::mem::take(&mut task.listeners)
632                })
633                .unwrap_or_default()
634        };
635
636        for (pipeline_id, callback) in listeners {
637            callback(ImageCacheResponseMessage::VectorImageRasterizationComplete(
638                RasterizationCompleteResponse {
639                    pipeline_id,
640                    image_id: pending_image_id,
641                    requested_size,
642                },
643            ));
644        }
645    }
646
647    /// The rest of complete load. This requires that images have a valid `WebRenderImageKey`.
648    fn complete_load(&mut self, key: LoadKey, load_result: LoadResult) {
649        debug!("Completed decoding for {:?}", load_result);
650        let pending_load = match self.pending_loads.remove(&key) {
651            Some(load) => load,
652            None => return,
653        };
654        let url = pending_load.final_url.clone();
655        let image_response = match load_result {
656            LoadResult::LoadedRasterImage(raster_image) => {
657                assert!(raster_image.id.is_some());
658                ImageResponse::Loaded(Image::Raster(Arc::new(raster_image)), url.unwrap())
659            },
660            LoadResult::LoadedVectorImage(vector_image) => {
661                self.vector_images.insert(key, vector_image.clone());
662                let natural_dimensions = vector_image.svg_tree.size().to_int_size();
663                let metadata = ImageMetadata {
664                    width: natural_dimensions.width(),
665                    height: natural_dimensions.height(),
666                };
667
668                let vector_image = VectorImage {
669                    id: key,
670                    svg_id: None,
671                    metadata,
672                    cors_status: vector_image.cors_status,
673                };
674                ImageResponse::Loaded(Image::Vector(vector_image), url.unwrap())
675            },
676            LoadResult::FailedToLoadOrDecode => ImageResponse::FailedToLoadOrDecode,
677        };
678
679        let completed_load = CompletedLoad::new(image_response.clone(), key);
680        self.completed_loads.insert(
681            (
682                pending_load.url,
683                pending_load.load_origin,
684                pending_load.cors_setting,
685            ),
686            completed_load,
687        );
688
689        for listener in pending_load.listeners {
690            listener.respond(image_response.clone());
691        }
692    }
693
694    fn remove_loaded_image(
695        &mut self,
696        url: &ServoUrl,
697        origin: &ImmutableOrigin,
698        cors_setting: &Option<CorsSettings>,
699    ) {
700        if let Some(loaded_image) =
701            self.completed_loads
702                .remove(&(url.clone(), origin.clone(), *cors_setting)) &&
703            let ImageResponse::Loaded(Image::Raster(image), _) = loaded_image.image_response &&
704            let Some(id) = image.id
705        {
706            self.paint_api.update_images(
707                self.webview_id.into(),
708                vec![ImageUpdate::DeleteImage(id)].into(),
709            );
710        }
711    }
712
713    fn remove_rasterized_vector_image(
714        &mut self,
715        image_id: &PendingImageId,
716        device_size: &DeviceIntSize,
717    ) {
718        if let Some(entry) = self
719            .rasterized_vector_images
720            .remove(&(*image_id, *device_size))
721        {
722            if let Some(result) = entry.result {
723                if let Some(image_id) = result.id {
724                    self.paint_api.update_images(
725                        self.webview_id.into(),
726                        vec![ImageUpdate::DeleteImage(image_id)].into(),
727                    );
728                }
729            } else {
730                // If there is no corresponding rasterized_vector_image result,
731                // then the vector image is either being rasterized or is in
732                // self.store.key_cache.pending_image_keys. Either way, we need to notify the
733                // KeyCache that it was evicted.
734                self.evict_image_from_keycache(image_id, device_size);
735            }
736        } else {
737            // If there is no corresponding rasterized_vector_image result,
738            // then the vector image is either being rasterized or is in
739            // self.store.key_cache.pending_image_keys. Either way, we need to notify the
740            // KeyCache that it was evicted.
741            self.evict_image_from_keycache(image_id, device_size);
742        }
743    }
744
745    /// Return a completed image if it exists, or None if there is no complete load
746    /// or the complete load is not fully decoded or is unavailable.
747    fn get_completed_image_if_available(
748        &self,
749        url: ServoUrl,
750        origin: ImmutableOrigin,
751        cors_setting: Option<CorsSettings>,
752    ) -> Option<Result<(Image, ServoUrl), ()>> {
753        self.completed_loads
754            .get(&(url, origin, cors_setting))
755            .map(|completed_load| match &completed_load.image_response {
756                ImageResponse::Loaded(image, url) => Ok((image.clone(), url.clone())),
757                ImageResponse::FailedToLoadOrDecode | ImageResponse::MetadataLoaded(_) => Err(()),
758            })
759    }
760
761    /// Handle a message from one of the decoder worker threads or from a sync
762    /// decoding operation.
763    fn handle_decoder(&mut self, msg: DecoderMsg) {
764        let image = match msg.image {
765            None => LoadResult::FailedToLoadOrDecode,
766            Some(DecodedImage::Raster(raster_image)) => {
767                self.load_image_with_keycache(PendingKey::RasterImage((msg.key, raster_image)));
768                return;
769            },
770            Some(DecodedImage::Vector(vector_image_data)) => {
771                LoadResult::LoadedVectorImage(vector_image_data)
772            },
773        };
774        self.complete_load(msg.key, image);
775    }
776}
777
778pub struct ImageCacheFactoryImpl {
779    /// The data to use for the broken image icon used when images cannot load.
780    broken_image_icon_data: Arc<Vec<u8>>,
781    /// Thread pool for image decoding
782    thread_pool: Arc<ThreadPool>,
783    /// A shared font database to be used by system fonts accessed when rasterizing vector
784    /// images.
785    fontdb: Arc<fontdb::Database>,
786}
787
788impl ImageCacheFactoryImpl {
789    pub fn new(broken_image_icon_data: Vec<u8>) -> Self {
790        debug!("Creating new ImageCacheFactoryImpl");
791        let mut fontdb = fontdb::Database::new();
792        fontdb.load_system_fonts();
793
794        Self {
795            broken_image_icon_data: Arc::new(broken_image_icon_data),
796            thread_pool: ThreadPool::global(),
797            fontdb: Arc::new(fontdb),
798        }
799    }
800}
801
802impl ImageCacheFactory for ImageCacheFactoryImpl {
803    fn create(
804        &self,
805        webview_id: WebViewId,
806        pipeline_id: PipelineId,
807        paint_api: &CrossProcessPaintApi,
808    ) -> Arc<dyn ImageCache> {
809        Arc::new(ImageCacheImpl {
810            store: Arc::new(Mutex::new(ImageCacheStore {
811                pending_loads: AllPendingLoads::new(),
812                completed_loads: HashMap::new(),
813                vector_images: FxHashMap::default(),
814                rasterized_vector_images: FxHashMap::default(),
815                broken_image_icon_image: OnceCell::new(),
816                paint_api: paint_api.clone(),
817                pipeline_id,
818                webview_id,
819                key_cache: KeyCache::new(),
820                svg_rasterization_task_store: SvgRasterizationTaskStore::default(),
821            })),
822            svg_id_image_id_map: Arc::new(Mutex::new(FxHashMap::default())),
823            broken_image_icon_data: self.broken_image_icon_data.clone(),
824            thread_pool: self.thread_pool.clone(),
825            fontdb: self.fontdb.clone(),
826        })
827    }
828}
829
830pub struct ImageCacheImpl {
831    /// Per-[`ImageCache`] data.
832    store: Arc<Mutex<ImageCacheStore>>,
833    /// Maps an SVGElement uuid to a pending image id in the store
834    svg_id_image_id_map: Arc<Mutex<FxHashMap<String, PendingImageId>>>,
835    /// The data to use for the broken image icon used when images cannot load.
836    broken_image_icon_data: Arc<Vec<u8>>,
837    /// Thread pool for image decoding. This is shared with other [`ImageCache`]s in the
838    /// same process.
839    thread_pool: Arc<ThreadPool>,
840    /// A shared font database to be used by system fonts accessed when rasterizing vector
841    /// images. This is shared with other [`ImageCache`]s in the same process.
842    fontdb: Arc<fontdb::Database>,
843}
844
845impl ImageCache for ImageCacheImpl {
846    fn memory_reports(&self, prefix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> {
847        let store_size = self.store.lock().size_of(ops);
848        let fontdb_size = self.fontdb.conditional_size_of(ops);
849        vec![
850            Report {
851                path: path![prefix, "image-cache"],
852                kind: ReportKind::ExplicitSystemHeapSize,
853                size: store_size,
854            },
855            Report {
856                path: path![prefix, "image-cache", "fontdb"],
857                kind: ReportKind::ExplicitSystemHeapSize,
858                size: fontdb_size,
859            },
860        ]
861    }
862
863    #[cfg(feature = "test-util")]
864    fn number_of_rasterize_tasks(&self) -> usize {
865        self.store.lock().number_of_rasterize_tasks()
866    }
867
868    fn get_image_key(&self) -> Option<WebRenderImageKey> {
869        let mut store = self.store.lock();
870        if let KeyCacheState::Ready(ref mut cache) = store.key_cache.cache {
871            if let Some(image_key) = cache.pop() {
872                return Some(image_key);
873            }
874
875            store.fetch_more_image_keys();
876        }
877
878        store
879            .paint_api
880            .generate_image_key_blocking(store.webview_id)
881    }
882
883    fn get_image(
884        &self,
885        url: ServoUrl,
886        origin: ImmutableOrigin,
887        cors_setting: Option<CorsSettings>,
888    ) -> Option<Image> {
889        let store = self.store.lock();
890        let result = store.get_completed_image_if_available(url, origin, cors_setting);
891        match result {
892            Some(Ok((img, _))) => Some(img),
893            _ => None,
894        }
895    }
896
897    fn get_cached_image_status(
898        &self,
899        url: ServoUrl,
900        origin: ImmutableOrigin,
901        cors_setting: Option<CorsSettings>,
902    ) -> ImageCacheResult {
903        let mut store = self.store.lock();
904        if let Some(result) =
905            store.get_completed_image_if_available(url.clone(), origin.clone(), cors_setting)
906        {
907            match result {
908                Ok((image, image_url)) => {
909                    debug!("{} is available", url);
910                    return ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
911                        image,
912                        url: image_url,
913                    });
914                },
915                Err(()) => {
916                    debug!("{} is not available", url);
917                    return ImageCacheResult::FailedToLoadOrDecode;
918                },
919            }
920        }
921
922        let result = store
923            .pending_loads
924            .get_cached(url.clone(), origin, cors_setting);
925        match result {
926            CacheResult::Hit(key, pl) => match (&pl.result, &pl.metadata) {
927                (&Some(Ok(_)), _) => ImageCacheResult::Pending(key),
928                (&None, Some(meta)) => {
929                    debug!("Metadata available for {} ({:?})", url, key);
930                    ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
931                        *meta, key,
932                    ))
933                },
934                (&Some(Err(_)), _) | (&None, &None) => {
935                    debug!("{} ({:?}) is still pending", url, key);
936                    ImageCacheResult::Pending(key)
937                },
938            },
939            CacheResult::Miss(Some((key, _pl))) => {
940                debug!("Should be requesting {} ({:?})", url, key);
941                ImageCacheResult::ReadyForRequest(key)
942            },
943            CacheResult::Miss(None) => {
944                debug!("Couldn't find an entry for {}", url);
945                ImageCacheResult::FailedToLoadOrDecode
946            },
947        }
948    }
949
950    fn add_rasterization_complete_listener(
951        &self,
952        pipeline_id: PipelineId,
953        image_id: PendingImageId,
954        requested_size: DeviceIntSize,
955        callback: ImageCacheResponseCallback,
956    ) {
957        {
958            let mut store = self.store.lock();
959            let key = (image_id, requested_size);
960            if !store.vector_images.contains_key(&image_id) {
961                warn!("Unknown image requested for rasterization for key {key:?}");
962                return;
963            };
964
965            let Some(task) = store.rasterized_vector_images.get_mut(&key) else {
966                warn!("Image rasterization task not found in the cache for key {key:?}");
967                return;
968            };
969
970            // If `result` is `None`, the task is still pending.
971            if task.result.is_none() {
972                task.listeners.push((pipeline_id, callback));
973                return;
974            }
975        }
976
977        callback(ImageCacheResponseMessage::VectorImageRasterizationComplete(
978            RasterizationCompleteResponse {
979                pipeline_id,
980                image_id,
981                requested_size,
982            },
983        ));
984    }
985
986    fn rasterize_vector_image(
987        &self,
988        image_id: PendingImageId,
989        requested_size: DeviceIntSize,
990        svg_id: Option<String>,
991    ) -> Option<RasterImage> {
992        let mut store = self.store.lock();
993        let Some(vector_image) = store.vector_images.get(&image_id).cloned() else {
994            warn!("Unknown image id {image_id:?} requested for rasterization");
995            return None;
996        };
997
998        // This early return relies on the fact that the result of image rasterization cannot
999        // ever be `None`. If that were the case we would need to check whether the entry
1000        // in the `HashMap` was `Occupied` or not.
1001        let entry = store
1002            .rasterized_vector_images
1003            .entry((image_id, requested_size))
1004            .or_default();
1005        if let Some(result) = entry.result.as_ref() {
1006            return Some(result.clone());
1007        }
1008
1009        if let Some(svg_id) = svg_id &&
1010            let Some(old_mapped_image_id) =
1011                self.svg_id_image_id_map.lock().insert(svg_id, image_id) &&
1012            old_mapped_image_id != image_id
1013        {
1014            store.vector_images.remove(&old_mapped_image_id);
1015            store
1016                .rasterized_vector_images
1017                .remove(&(old_mapped_image_id, requested_size));
1018            store
1019                .svg_rasterization_task_store
1020                .remove_all_for_id(old_mapped_image_id);
1021        }
1022
1023        if store
1024            .svg_rasterization_task_store
1025            .is_or_set_being_rasterized(image_id, requested_size)
1026        {
1027            return None;
1028        }
1029
1030        let store = self.store.clone();
1031        self.thread_pool.spawn(move || {
1032            let natural_size = vector_image.svg_tree.size().to_int_size();
1033            let tinyskia_requested_size = {
1034                let width = requested_size
1035                    .width
1036                    .try_into()
1037                    .unwrap_or(0)
1038                    .min(MAX_SVG_PIXMAP_DIMENSION);
1039                let height = requested_size
1040                    .height
1041                    .try_into()
1042                    .unwrap_or(0)
1043                    .min(MAX_SVG_PIXMAP_DIMENSION);
1044                tiny_skia::IntSize::from_wh(width, height).unwrap_or(natural_size)
1045            };
1046            let transform = tiny_skia::Transform::from_scale(
1047                tinyskia_requested_size.width() as f32 / natural_size.width() as f32,
1048                tinyskia_requested_size.height() as f32 / natural_size.height() as f32,
1049            );
1050            let mut pixmap = tiny_skia::Pixmap::new(
1051                tinyskia_requested_size.width(),
1052                tinyskia_requested_size.height(),
1053            )
1054            .unwrap();
1055            resvg::render(&vector_image.svg_tree, transform, &mut pixmap.as_mut());
1056
1057            let bytes = pixmap.take();
1058            let frame = ImageFrame {
1059                delay: None,
1060                byte_range: 0..bytes.len(),
1061                width: tinyskia_requested_size.width(),
1062                height: tinyskia_requested_size.height(),
1063            };
1064
1065            let rasterized_image = RasterImage {
1066                metadata: ImageMetadata {
1067                    width: tinyskia_requested_size.width(),
1068                    height: tinyskia_requested_size.height(),
1069                },
1070                format: PixelFormat::RGBA8,
1071                frames: vec![frame],
1072                bytes: Arc::new(bytes),
1073                id: None,
1074                cors_status: vector_image.cors_status,
1075                is_opaque: false,
1076                loop_count: None,
1077            };
1078
1079            let mut store = store.lock();
1080            store.load_image_with_keycache(PendingKey::Svg((
1081                image_id,
1082                rasterized_image,
1083                requested_size,
1084            )));
1085        });
1086        None
1087    }
1088
1089    /// Add a new listener for the given pending image id. If the image is already present,
1090    /// the responder will still receive the expected response.
1091    fn add_listener(&self, listener: ImageLoadListener) {
1092        let mut store = self.store.lock();
1093        self.add_listener_with_store(&mut store, listener);
1094    }
1095
1096    fn evict_completed_image(
1097        &self,
1098        url: &ServoUrl,
1099        origin: &ImmutableOrigin,
1100        cors_setting: &Option<CorsSettings>,
1101    ) {
1102        let mut store = self.store.lock();
1103        store.remove_loaded_image(url, origin, cors_setting);
1104    }
1105
1106    fn evict_rasterized_image(&self, svg_id: &str) {
1107        let mut store = self.store.lock();
1108        if let Some(mapped_image_id) = self.svg_id_image_id_map.lock().remove(svg_id) {
1109            store.pending_loads.remove(&mapped_image_id);
1110            store.vector_images.remove(&mapped_image_id);
1111            let images_to_remove = store
1112                .rasterized_vector_images
1113                .keys()
1114                .filter(|(id, _size)| *id == mapped_image_id)
1115                .cloned()
1116                .collect::<Vec<_>>();
1117            for (id, requested_size) in images_to_remove {
1118                store.remove_rasterized_vector_image(&id, &requested_size);
1119            }
1120        }
1121    }
1122
1123    /// Inform the image cache about a response for a pending request.
1124    fn notify_pending_response(&self, id: PendingImageId, action: FetchResponseMsg) {
1125        match (action, id) {
1126            (FetchResponseMsg::ProcessRequestBody(..), _) |
1127            (FetchResponseMsg::ProcessCspViolations(..), _) => (),
1128            (FetchResponseMsg::ProcessResponse(_, response), _) => {
1129                debug!("Received {:?} for {:?}", response.as_ref().map(|_| ()), id);
1130                let mut store = self.store.lock();
1131                if let Some(pending_load) = store.pending_loads.get_by_key_mut(&id) {
1132                    let (cors_status, metadata) = match response {
1133                        Ok(meta) => match meta {
1134                            FetchMetadata::Unfiltered(m) => (CorsStatus::Safe, Some(m)),
1135                            FetchMetadata::Filtered { unsafe_, filtered } => (
1136                                match filtered {
1137                                    FilteredMetadata::Basic(_) | FilteredMetadata::Cors(_) => {
1138                                        CorsStatus::Safe
1139                                    },
1140                                    FilteredMetadata::Opaque |
1141                                    FilteredMetadata::OpaqueRedirect(_) => CorsStatus::Unsafe,
1142                                },
1143                                Some(unsafe_),
1144                            ),
1145                        },
1146                        Err(_) => (CorsStatus::Unsafe, None),
1147                    };
1148                    let final_url = metadata.as_ref().map(|m| m.final_url.clone());
1149                    pending_load.final_url = final_url;
1150                    pending_load.cors_status = cors_status;
1151                    pending_load.content_type = metadata
1152                        .as_ref()
1153                        .and_then(|metadata| metadata.content_type.clone())
1154                        .map(|content_type| content_type.into_inner().into());
1155                } else {
1156                    debug!("Pending load for id {:?} already evicted from cache", id);
1157                }
1158            },
1159            (FetchResponseMsg::ProcessResponseChunk(_, data), _) => {
1160                debug!("Got some data for {:?}", id);
1161                let mut store = self.store.lock();
1162                if let Some(pending_load) = store.pending_loads.get_by_key_mut(&id) {
1163                    pending_load.bytes.extend_from_slice(&data);
1164
1165                    // jmr0 TODO: possibly move to another task?
1166                    if pending_load.metadata.is_none() {
1167                        let mut reader = std::io::Cursor::new(pending_load.bytes.as_slice());
1168                        if let Ok(info) = imsz_from_reader(&mut reader) {
1169                            let img_metadata = ImageMetadata {
1170                                width: info.width as u32,
1171                                height: info.height as u32,
1172                            };
1173                            for listener in &pending_load.listeners {
1174                                listener.respond(ImageResponse::MetadataLoaded(img_metadata));
1175                            }
1176                            pending_load.metadata = Some(img_metadata);
1177                        }
1178                    }
1179                } else {
1180                    debug!("Pending load for id {:?} already evicted from cache", id);
1181                }
1182            },
1183            (FetchResponseMsg::ProcessResponseEOF(_, result, _), key) => {
1184                debug!("Received EOF for {:?}", key);
1185                match result {
1186                    Ok(_) => {
1187                        let (bytes, cors_status, content_type) = {
1188                            let mut store = self.store.lock();
1189                            if let Some(pending_load) = store.pending_loads.get_by_key_mut(&id) {
1190                                pending_load.result = Some(Ok(()));
1191                                debug!("Async decoding {} ({:?})", pending_load.url, key);
1192                                (
1193                                    pending_load.bytes.mark_complete(),
1194                                    pending_load.cors_status,
1195                                    pending_load.content_type.clone(),
1196                                )
1197                            } else {
1198                                debug!("Pending load for id {:?} already evicted from cache", id);
1199                                return;
1200                            }
1201                        };
1202
1203                        let local_store = self.store.clone();
1204                        let fontdb = self.fontdb.clone();
1205                        self.thread_pool.spawn(move || {
1206                            let msg =
1207                                decode_bytes_sync(key, &bytes, cors_status, content_type, fontdb);
1208                            local_store.lock().handle_decoder(msg);
1209                        });
1210                    },
1211                    Err(error) => {
1212                        debug!("Processing error for {key:?}: {error:?}");
1213                        let mut store = self.store.lock();
1214                        store.complete_load(id, LoadResult::FailedToLoadOrDecode)
1215                    },
1216                }
1217            },
1218        }
1219    }
1220
1221    /// This method does not block
1222    fn dispatch_fill_key_cache_with_batch_of_keys(&self, image_keys: Vec<WebRenderImageKey>) {
1223        // This is safe to do because of the following reason.
1224        // The only way this can be in a unwelcome state is the following chain of events
1225        // dispatch_fill_key -> get_image_key -> fetch_image_keys -> insert_keys_and_load_images.
1226        // However, we ignore all calls for this when the state is set to processing. Returning
1227        // the state to anything else enforces that we have the exclusive write access to the KeyCache.
1228        self.store.lock().key_cache.cache = KeyCacheState::Processing;
1229
1230        let store = self.store.clone();
1231        self.thread_pool.spawn(move || {
1232            store.lock().insert_keys_and_load_images(image_keys);
1233        });
1234    }
1235
1236    fn clear(&self) {
1237        self.store.lock().clear();
1238    }
1239
1240    fn get_broken_image_icon(&self) -> Option<Arc<RasterImage>> {
1241        let store = self.store.lock();
1242        store
1243            .broken_image_icon_image
1244            .get_or_init(|| {
1245                let mut image = load_from_memory(&self.broken_image_icon_data, CorsStatus::Unsafe)
1246                    .or_else(|| load_from_memory(FALLBACK_RIPPY, CorsStatus::Unsafe))?;
1247                let image_key = store
1248                    .paint_api
1249                    .generate_image_key_blocking(store.webview_id)
1250                    .expect("Could not generate image key for broken image icon");
1251                set_webrender_image_key(&store.paint_api, &mut image, image_key);
1252                Some(Arc::new(image))
1253            })
1254            .clone()
1255    }
1256}
1257
1258impl ImageCacheStore {
1259    /// Clear the image cache.
1260    fn clear(&mut self) {
1261        let deletions: smallvec::SmallVec<_> = self
1262            .completed_loads
1263            .values()
1264            .filter_map(|load| match &load.image_response {
1265                ImageResponse::Loaded(Image::Raster(image), _) => {
1266                    image.id.map(ImageUpdate::DeleteImage)
1267                },
1268                _ => None,
1269            })
1270            .chain(
1271                self.rasterized_vector_images
1272                    .values()
1273                    .filter_map(|task| task.result.as_ref()?.id.map(ImageUpdate::DeleteImage)),
1274            )
1275            .chain(
1276                self.broken_image_icon_image
1277                    .get()
1278                    .and_then(|icon| icon.as_ref())
1279                    .and_then(|icon| icon.id)
1280                    .map(ImageUpdate::DeleteImage),
1281            )
1282            .collect();
1283        if !deletions.is_empty() {
1284            self.paint_api
1285                .update_images(self.webview_id.into(), deletions);
1286        }
1287        // Clear these fields, since `clear()` will be called multiple times,
1288        // explicitly on pipeline close, and again on Drop (as a safeguard,
1289        // since we could forget to explicitly clear).
1290        self.completed_loads.clear();
1291        self.rasterized_vector_images.clear();
1292        let _ = self.broken_image_icon_image.take();
1293    }
1294}
1295
1296impl Drop for ImageCacheStore {
1297    fn drop(&mut self) {
1298        self.clear();
1299    }
1300}
1301
1302impl ImageCacheImpl {
1303    /// Require self.store.lock() before calling.
1304    fn add_listener_with_store(&self, store: &mut ImageCacheStore, listener: ImageLoadListener) {
1305        let id = listener.id;
1306        if let Some(load) = store.pending_loads.get_by_key_mut(&id) {
1307            if let Some(ref metadata) = load.metadata {
1308                listener.respond(ImageResponse::MetadataLoaded(*metadata));
1309            }
1310            load.add_listener(listener);
1311            return;
1312        }
1313        if let Some(load) = store.completed_loads.values().find(|l| l.id == id) {
1314            listener.respond(load.image_response.clone());
1315            return;
1316        }
1317        warn!("Couldn't find cached entry for listener {:?}", id);
1318    }
1319}