Skip to main content

net/
image_cache.rs

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