1use std::collections::HashMap;
6use std::sync::Arc;
7
8use base::id::RenderingGroupId;
9use embedder_traits::UntrustedNodeAddress;
10use euclid::Size2D;
11use fonts::FontContext;
12use layout_api::wrapper_traits::ThreadSafeLayoutNode;
13use layout_api::{
14 AnimatingImages, IFrameSizes, LayoutImageDestination, PendingImage, PendingImageState,
15 PendingRasterizationImage,
16};
17use net_traits::image_cache::{
18 Image as CachedImage, ImageCache, ImageCacheResult, ImageOrMetadataAvailable, PendingImageId,
19 UsePlaceholder,
20};
21use parking_lot::{Mutex, RwLock};
22use pixels::RasterImage;
23use script::layout_dom::ServoThreadSafeLayoutNode;
24use servo_url::{ImmutableOrigin, ServoUrl};
25use style::context::SharedStyleContext;
26use style::dom::OpaqueNode;
27use style::values::computed::image::{Gradient, Image};
28use webrender_api::units::{DeviceIntSize, DeviceSize};
29
30pub(crate) type CachedImageOrError = Result<CachedImage, ResolveImageError>;
31
32pub(crate) struct LayoutContext<'a> {
33 pub use_rayon: bool,
34
35 pub style_context: SharedStyleContext<'a>,
37
38 pub font_context: Arc<FontContext>,
40
41 pub iframe_sizes: Mutex<IFrameSizes>,
43
44 pub image_resolver: Arc<ImageResolver>,
47
48 pub rendering_group_id: RenderingGroupId,
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, UsePlaceholder), 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 use_placeholder: UsePlaceholder,
133 destination: LayoutImageDestination,
134 ) -> LayoutImageCacheResult {
135 let cache_result = self.image_cache.get_cached_image_status(
137 url.clone(),
138 self.origin.clone(),
139 None,
140 use_placeholder,
141 );
142
143 match cache_result {
144 ImageCacheResult::Available(img_or_meta) => {
145 LayoutImageCacheResult::DataAvailable(img_or_meta)
146 },
147 ImageCacheResult::Pending(id) => {
150 let image = PendingImage {
151 state: PendingImageState::PendingResponse,
152 node: node.into(),
153 id,
154 origin: self.origin.clone(),
155 destination,
156 };
157 self.pending_images.lock().push(image);
158 LayoutImageCacheResult::Pending
159 },
160 ImageCacheResult::ReadyForRequest(id) => {
162 let image = PendingImage {
163 state: PendingImageState::Unrequested(url),
164 node: node.into(),
165 id,
166 origin: self.origin.clone(),
167 destination,
168 };
169 self.pending_images.lock().push(image);
170 LayoutImageCacheResult::Pending
171 },
172 ImageCacheResult::LoadError => LayoutImageCacheResult::LoadError,
174 }
175 }
176
177 pub(crate) fn handle_animated_image(&self, node: OpaqueNode, image: Arc<RasterImage>) {
178 let mut animating_images = self.animating_images.write();
179 if !image.should_animate() {
180 animating_images.remove(node);
181 } else {
182 animating_images.maybe_insert_or_update(node, image, self.animation_timeline_value);
183 }
184 }
185
186 pub(crate) fn get_cached_image_for_url(
187 &self,
188 node: OpaqueNode,
189 url: ServoUrl,
190 use_placeholder: UsePlaceholder,
191 destination: LayoutImageDestination,
192 ) -> Result<CachedImage, ResolveImageError> {
193 if let Some(cached_image) = self
194 .resolved_images_cache
195 .read()
196 .get(&(url.clone(), use_placeholder))
197 {
198 return cached_image.clone();
199 }
200
201 let result =
202 self.get_or_request_image_or_meta(node, url.clone(), use_placeholder, destination);
203 match result {
204 LayoutImageCacheResult::DataAvailable(img_or_meta) => match img_or_meta {
205 ImageOrMetadataAvailable::ImageAvailable { image, .. } => {
206 if let Some(image) = image.as_raster_image() {
207 self.handle_animated_image(node, image.clone());
208 }
209
210 let mut resolved_images_cache = self.resolved_images_cache.write();
211 resolved_images_cache.insert((url, use_placeholder), Ok(image.clone()));
212 Ok(image)
213 },
214 ImageOrMetadataAvailable::MetadataAvailable(..) => {
215 Result::Err(ResolveImageError::OnlyMetadata)
216 },
217 },
218 LayoutImageCacheResult::Pending => Result::Err(ResolveImageError::ImagePending),
219 LayoutImageCacheResult::LoadError => {
220 let error = Err(ResolveImageError::LoadError);
221 self.resolved_images_cache
222 .write()
223 .insert((url, use_placeholder), error.clone());
224 error
225 },
226 }
227 }
228
229 pub(crate) fn rasterize_vector_image(
230 &self,
231 image_id: PendingImageId,
232 size: DeviceIntSize,
233 node: OpaqueNode,
234 ) -> Option<RasterImage> {
235 let result = self.image_cache.rasterize_vector_image(image_id, size);
236 if result.is_none() {
237 self.pending_rasterization_images
238 .lock()
239 .push(PendingRasterizationImage {
240 id: image_id,
241 node: node.into(),
242 size,
243 });
244 }
245 result
246 }
247
248 pub(crate) fn queue_svg_element_for_serialization(
249 &self,
250 element: ServoThreadSafeLayoutNode<'_>,
251 ) {
252 self.pending_svg_elements_for_serialization
253 .lock()
254 .push(element.opaque().into())
255 }
256
257 pub(crate) fn resolve_image<'a>(
258 &self,
259 node: Option<OpaqueNode>,
260 image: &'a Image,
261 ) -> Result<ResolvedImage<'a>, ResolveImageError> {
262 match image {
263 Image::None => Result::Err(ResolveImageError::None),
265 Image::CrossFade(_) => Result::Err(ResolveImageError::NotImplementedYet),
266 Image::PaintWorklet(_) => Result::Err(ResolveImageError::NotImplementedYet),
267 Image::Gradient(gradient) => Ok(ResolvedImage::Gradient(gradient)),
268 Image::Url(image_url) => {
269 let image_url = image_url.url().ok_or(ResolveImageError::InvalidUrl)?;
276 let node = node.ok_or(ResolveImageError::MissingNode)?;
277 let image = self.get_cached_image_for_url(
278 node,
279 image_url.clone().into(),
280 UsePlaceholder::No,
281 LayoutImageDestination::DisplayListBuilding,
282 )?;
283 let metadata = image.metadata();
284 let size = Size2D::new(metadata.width, metadata.height).to_f32();
285 Ok(ResolvedImage::Image { image, size })
286 },
287 Image::ImageSet(image_set) => {
288 image_set
289 .items
290 .get(image_set.selected_index)
291 .ok_or(ResolveImageError::ImageMissingFromImageSet)
292 .and_then(|image| {
293 self.resolve_image(node, &image.image)
294 .map(|info| match info {
295 ResolvedImage::Image {
296 image: cached_image,
297 ..
298 } => {
299 let image_metadata = cached_image.metadata();
306 let size = if cached_image.as_raster_image().is_some() {
307 let scale_factor = image.resolution.dppx();
308 Size2D::new(
309 image_metadata.width as f32 / scale_factor,
310 image_metadata.height as f32 / scale_factor,
311 )
312 } else {
313 Size2D::new(image_metadata.width, image_metadata.height)
314 .to_f32()
315 };
316
317 ResolvedImage::Image {
318 image: cached_image,
319 size,
320 }
321 },
322 _ => info,
323 })
324 })
325 },
326 Image::LightDark(..) => unreachable!("light-dark() should be disabled"),
327 }
328 }
329}