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