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