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