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