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