Skip to main content

net/
image_cache.rs

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