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