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