use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::sync::{Arc, Mutex};
use std::thread;
use base::id::PipelineId;
use fnv::FnvHasher;
use fonts::FontContext;
use net_traits::image_cache::{
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
};
use parking_lot::RwLock;
use script_layout_interface::{PendingImage, PendingImageState};
use script_traits::Painter;
use servo_atoms::Atom;
use servo_url::{ImmutableOrigin, ServoUrl};
use style::context::{RegisteredSpeculativePainter, SharedStyleContext};
use crate::display_list::items::{OpaqueNode, WebRenderImageInfo};
type WebrenderImageCache =
HashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo, BuildHasherDefault<FnvHasher>>;
pub struct LayoutContext<'a> {
pub id: PipelineId,
pub origin: ImmutableOrigin,
pub style_context: SharedStyleContext<'a>,
pub image_cache: Arc<dyn ImageCache>,
pub font_context: Arc<FontContext>,
pub webrender_image_cache: Arc<RwLock<WebrenderImageCache>>,
pub registered_painters: &'a dyn RegisteredPainters,
pub pending_images: Mutex<Vec<PendingImage>>,
}
impl<'a> Drop for LayoutContext<'a> {
fn drop(&mut self) {
if !thread::panicking() {
assert!(self.pending_images.lock().unwrap().is_empty());
}
}
}
impl<'a> LayoutContext<'a> {
#[inline(always)]
pub fn shared_context(&self) -> &SharedStyleContext {
&self.style_context
}
pub fn get_or_request_image_or_meta(
&self,
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder,
) -> Option<ImageOrMetadataAvailable> {
let cache_result = self.image_cache.get_cached_image_status(
url.clone(),
self.origin.clone(),
None,
use_placeholder,
);
match cache_result {
ImageCacheResult::Available(img_or_meta) => Some(img_or_meta),
ImageCacheResult::Pending(id) => {
let image = PendingImage {
state: PendingImageState::PendingResponse,
node: node.into(),
id,
origin: self.origin.clone(),
};
self.pending_images.lock().unwrap().push(image);
None
},
ImageCacheResult::ReadyForRequest(id) => {
let image = PendingImage {
state: PendingImageState::Unrequested(url),
node: node.into(),
id,
origin: self.origin.clone(),
};
self.pending_images.lock().unwrap().push(image);
None
},
ImageCacheResult::LoadError => None,
}
}
pub fn get_webrender_image_for_url(
&self,
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder,
) -> Option<WebRenderImageInfo> {
if let Some(existing_webrender_image) = self
.webrender_image_cache
.read()
.get(&(url.clone(), use_placeholder))
{
return Some(*existing_webrender_image);
}
match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) {
Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => {
let image_info = WebRenderImageInfo::from_image(&image);
if image_info.key.is_none() {
Some(image_info)
} else {
let mut webrender_image_cache = self.webrender_image_cache.write();
webrender_image_cache.insert((url, use_placeholder), image_info);
Some(image_info)
}
},
None | Some(ImageOrMetadataAvailable::MetadataAvailable(_)) => None,
}
}
}
pub trait RegisteredPainter: RegisteredSpeculativePainter + Painter {}
pub trait RegisteredPainters: Sync {
fn get(&self, name: &Atom) -> Option<&dyn RegisteredPainter>;
}