1use std::collections::HashMap;
6use std::sync::Arc;
7
8use embedder_traits::UntrustedNodeAddress;
9use euclid::Size2D;
10use fonts::FontContext;
11use layout_api::{
12 AnimatingImages, IFrameSizes, LayoutImageDestination, LayoutNode, PendingImage,
13 PendingImageState, PendingRasterizationImage,
14};
15use net_traits::image_cache::{
16 Image as CachedImage, ImageCache, ImageCacheResult, ImageOrMetadataAvailable, PendingImageId,
17};
18use net_traits::request::InternalRequest;
19use parking_lot::{Mutex, RwLock};
20use pixels::RasterImage;
21use script::layout_dom::ServoLayoutNode;
22use servo_base::id::PainterId;
23use servo_url::{ImmutableOrigin, ServoUrl};
24use style::context::SharedStyleContext;
25use style::dom::OpaqueNode;
26use style::values::computed::image::{Gradient, Image};
27use webrender_api::units::{DeviceIntSize, DeviceSize};
28
29pub(crate) type CachedImageOrError = Result<CachedImage, ResolveImageError>;
30
31pub(crate) struct LayoutContext<'a> {
32 pub use_rayon: bool,
33
34 pub style_context: SharedStyleContext<'a>,
36
37 pub font_context: Arc<FontContext>,
39
40 pub iframe_sizes: Mutex<IFrameSizes>,
42
43 pub image_resolver: Arc<ImageResolver>,
46
47 pub painter_id: PainterId,
49}
50
51pub enum ResolvedImage<'a> {
52 Gradient(&'a Gradient),
53 Image {
56 image: CachedImage,
57 size: DeviceSize,
58 },
59}
60
61#[derive(Clone, Copy, Debug)]
62pub enum ResolveImageError {
63 LoadError,
64 ImagePending,
65 OnlyMetadata,
66 InvalidUrl,
67 MissingNode,
68 ImageMissingFromImageSet,
69 NotImplementedYet,
70 None,
71}
72
73pub(crate) enum LayoutImageCacheResult {
74 Pending,
75 DataAvailable(ImageOrMetadataAvailable),
76 LoadError,
77}
78
79pub(crate) struct ImageResolver {
80 pub origin: ImmutableOrigin,
82
83 pub image_cache: Arc<dyn ImageCache>,
85
86 pub pending_images: Mutex<Vec<PendingImage>>,
88
89 pub pending_rasterization_images: Mutex<Vec<PendingRasterizationImage>>,
92
93 pub pending_svg_elements_for_serialization: Mutex<Vec<UntrustedNodeAddress>>,
99
100 pub animating_images: Arc<RwLock<AnimatingImages>>,
104
105 pub resolved_images_cache: Arc<RwLock<HashMap<ServoUrl, CachedImageOrError>>>,
108
109 pub animation_timeline_value: f64,
111}
112
113impl Drop for ImageResolver {
114 fn drop(&mut self) {
115 if !std::thread::panicking() {
116 assert!(self.pending_images.lock().is_empty());
117 assert!(self.pending_rasterization_images.lock().is_empty());
118 assert!(
119 self.pending_svg_elements_for_serialization
120 .lock()
121 .is_empty()
122 );
123 }
124 }
125}
126
127impl ImageResolver {
128 pub(crate) fn get_or_request_image_or_meta(
129 &self,
130 node: OpaqueNode,
131 url: ServoUrl,
132 destination: LayoutImageDestination,
133 is_internal_request: InternalRequest,
134 ) -> LayoutImageCacheResult {
135 let cache_result =
137 self.image_cache
138 .get_cached_image_status(url.clone(), self.origin.clone(), None);
139
140 match cache_result {
141 ImageCacheResult::Available(img_or_meta) => {
142 LayoutImageCacheResult::DataAvailable(img_or_meta)
143 },
144 ImageCacheResult::Pending(id) => {
147 let image = PendingImage {
148 state: PendingImageState::PendingResponse,
149 node: node.into(),
150 id,
151 origin: self.origin.clone(),
152 destination,
153 is_internal_request,
154 };
155 self.pending_images.lock().push(image);
156 LayoutImageCacheResult::Pending
157 },
158 ImageCacheResult::ReadyForRequest(id) => {
160 let image = PendingImage {
161 state: PendingImageState::Unrequested(url),
162 node: node.into(),
163 id,
164 origin: self.origin.clone(),
165 destination,
166 is_internal_request,
167 };
168 self.pending_images.lock().push(image);
169 LayoutImageCacheResult::Pending
170 },
171 ImageCacheResult::FailedToLoadOrDecode => LayoutImageCacheResult::LoadError,
173 }
174 }
175
176 pub(crate) fn handle_animated_image(&self, node: OpaqueNode, image: Arc<RasterImage>) {
177 let mut animating_images = self.animating_images.write();
178 if !image.should_animate() {
179 animating_images.remove(node);
180 } else {
181 animating_images.maybe_insert_or_update(node, image, self.animation_timeline_value);
182 }
183 }
184
185 pub(crate) fn get_cached_image_for_url(
186 &self,
187 node: OpaqueNode,
188 url: ServoUrl,
189 destination: LayoutImageDestination,
190 is_internal_request: InternalRequest,
191 ) -> Result<CachedImage, ResolveImageError> {
192 if let Some(cached_image) = self.resolved_images_cache.read().get(&url) {
193 return cached_image.clone();
194 }
195
196 let result =
197 self.get_or_request_image_or_meta(node, url.clone(), destination, is_internal_request);
198 match result {
199 LayoutImageCacheResult::DataAvailable(img_or_meta) => match img_or_meta {
200 ImageOrMetadataAvailable::ImageAvailable { image, .. } => {
201 if let Some(image) = image.as_raster_image() {
202 self.handle_animated_image(node, image);
203 }
204
205 let mut resolved_images_cache = self.resolved_images_cache.write();
206 resolved_images_cache.insert(url, Ok(image.clone()));
207 Ok(image)
208 },
209 ImageOrMetadataAvailable::MetadataAvailable(..) => {
210 Result::Err(ResolveImageError::OnlyMetadata)
211 },
212 },
213 LayoutImageCacheResult::Pending => Result::Err(ResolveImageError::ImagePending),
214 LayoutImageCacheResult::LoadError => {
215 let error = Err(ResolveImageError::LoadError);
216 self.resolved_images_cache
217 .write()
218 .insert(url, error.clone());
219 error
220 },
221 }
222 }
223
224 pub(crate) fn rasterize_vector_image(
225 &self,
226 image_id: PendingImageId,
227 size: DeviceIntSize,
228 node: OpaqueNode,
229 svg_id: Option<String>,
230 ) -> Option<RasterImage> {
231 let result = self
232 .image_cache
233 .rasterize_vector_image(image_id, size, svg_id);
234 if result.is_none() {
235 self.pending_rasterization_images
236 .lock()
237 .push(PendingRasterizationImage {
238 id: image_id,
239 node: node.into(),
240 size,
241 });
242 }
243 result
244 }
245
246 pub(crate) fn queue_svg_element_for_serialization(&self, element: ServoLayoutNode<'_>) {
247 self.pending_svg_elements_for_serialization
248 .lock()
249 .push(element.opaque().into())
250 }
251
252 pub(crate) fn resolve_image<'a>(
253 &self,
254 node: Option<OpaqueNode>,
255 image: &'a Image,
256 ) -> Result<ResolvedImage<'a>, ResolveImageError> {
257 match image {
258 Image::None => Result::Err(ResolveImageError::None),
260 Image::CrossFade(_) => Result::Err(ResolveImageError::NotImplementedYet),
261 Image::PaintWorklet(_) => Result::Err(ResolveImageError::NotImplementedYet),
262 Image::Gradient(gradient) => Ok(ResolvedImage::Gradient(gradient)),
263 Image::Url(image_url) => {
264 let image_url = image_url.url().ok_or(ResolveImageError::InvalidUrl)?;
271 let node = node.ok_or(ResolveImageError::MissingNode)?;
272 let image = self.get_cached_image_for_url(
273 node,
274 image_url.clone().into(),
275 LayoutImageDestination::DisplayListBuilding,
276 InternalRequest::No,
277 )?;
278 let metadata = image.metadata();
279 let size = Size2D::new(metadata.width, metadata.height).to_f32();
280 Ok(ResolvedImage::Image { image, size })
281 },
282 Image::ImageSet(image_set) => {
283 image_set
284 .items
285 .get(image_set.selected_index)
286 .ok_or(ResolveImageError::ImageMissingFromImageSet)
287 .and_then(|image| {
288 self.resolve_image(node, &image.image)
289 .map(|info| match info {
290 ResolvedImage::Image {
291 image: cached_image,
292 ..
293 } => {
294 let image_metadata = cached_image.metadata();
301 let size = if cached_image.as_raster_image().is_some() {
302 let scale_factor = image.resolution.dppx();
303 Size2D::new(
304 image_metadata.width as f32 / scale_factor,
305 image_metadata.height as f32 / scale_factor,
306 )
307 } else {
308 Size2D::new(image_metadata.width, image_metadata.height)
309 .to_f32()
310 };
311
312 ResolvedImage::Image {
313 image: cached_image,
314 size,
315 }
316 },
317 _ => info,
318 })
319 })
320 },
321 Image::LightDark(..) => unreachable!("light-dark() should be disabled"),
322 }
323 }
324}