net/
image_cache.rs

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