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::color::Color;
27use style::values::computed::image::{Gradient, Image};
28use style_traits::DevicePixel;
29use webrender_api::units::{DeviceIntSize, DeviceSize};
30
31pub(crate) type CachedImageOrError = Result<CachedImage, ResolveImageError>;
32
33pub(crate) struct LayoutContext<'a> {
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 pub allow_parallel_layout: bool,
52
53 pub parallelism_job_count_minimum: usize,
56
57 pub parallelism_job_size_minimum: usize,
60
61 pub device_size: Size2D<f32, DevicePixel>,
63}
64
65impl LayoutContext<'_> {
66 pub(crate) fn should_parallelize(&self, number_of_jobs: usize) -> bool {
67 self.allow_parallel_layout && number_of_jobs >= self.parallelism_job_count_minimum
68 }
69
70 pub(crate) fn should_parallelize_layout(&self, jobs: impl Iterator<Item = usize>) -> bool {
71 self.allow_parallel_layout &&
72 jobs.filter(|job| *job >= self.parallelism_job_size_minimum)
73 .count() >=
74 self.parallelism_job_count_minimum
75 }
76}
77
78pub enum ResolvedImage<'a> {
79 Gradient(&'a Gradient),
80 Color(&'a Color),
81 Image {
84 image: CachedImage,
85 size: DeviceSize,
86 },
87}
88
89#[derive(Clone, Copy, Debug)]
90pub enum ResolveImageError {
91 LoadError,
92 ImagePending,
93 OnlyMetadata,
94 InvalidUrl,
95 MissingNode,
96 ImageMissingFromImageSet,
97 NotImplementedYet,
98 None,
99}
100
101pub(crate) enum LayoutImageCacheResult {
102 Pending,
103 DataAvailable(ImageOrMetadataAvailable),
104 LoadError,
105}
106
107pub(crate) struct ImageResolver {
108 pub origin: ImmutableOrigin,
110
111 pub image_cache: Arc<dyn ImageCache>,
113
114 pub pending_images: Mutex<Vec<PendingImage>>,
116
117 pub pending_rasterization_images: Mutex<Vec<PendingRasterizationImage>>,
120
121 pub pending_svg_elements_for_serialization: Mutex<Vec<UntrustedNodeAddress>>,
127
128 pub animating_images: Arc<RwLock<AnimatingImages>>,
132
133 pub resolved_images_cache: Arc<RwLock<HashMap<ServoUrl, CachedImageOrError>>>,
136
137 pub animation_timeline_value: f64,
139}
140
141impl Drop for ImageResolver {
142 fn drop(&mut self) {
143 if !std::thread::panicking() {
144 assert!(self.pending_images.lock().is_empty());
145 assert!(self.pending_rasterization_images.lock().is_empty());
146 assert!(
147 self.pending_svg_elements_for_serialization
148 .lock()
149 .is_empty()
150 );
151 }
152 }
153}
154
155impl ImageResolver {
156 pub(crate) fn get_or_request_image_or_meta(
157 &self,
158 node: OpaqueNode,
159 url: ServoUrl,
160 destination: LayoutImageDestination,
161 is_internal_request: InternalRequest,
162 ) -> LayoutImageCacheResult {
163 let cache_result =
165 self.image_cache
166 .get_cached_image_status(url.clone(), self.origin.clone(), None);
167
168 match cache_result {
169 ImageCacheResult::Available(img_or_meta) => {
170 LayoutImageCacheResult::DataAvailable(img_or_meta)
171 },
172 ImageCacheResult::Pending(id) => {
175 let image = PendingImage {
176 state: PendingImageState::PendingResponse,
177 node: node.into(),
178 id,
179 origin: self.origin.clone(),
180 destination,
181 is_internal_request,
182 };
183 self.pending_images.lock().push(image);
184 LayoutImageCacheResult::Pending
185 },
186 ImageCacheResult::ReadyForRequest(id) => {
188 let image = PendingImage {
189 state: PendingImageState::Unrequested(url),
190 node: node.into(),
191 id,
192 origin: self.origin.clone(),
193 destination,
194 is_internal_request,
195 };
196 self.pending_images.lock().push(image);
197 LayoutImageCacheResult::Pending
198 },
199 ImageCacheResult::FailedToLoadOrDecode => LayoutImageCacheResult::LoadError,
201 }
202 }
203
204 pub(crate) fn handle_animated_image(&self, node: OpaqueNode, image: Arc<RasterImage>) {
205 let mut animating_images = self.animating_images.write();
206 if !image.should_animate() {
207 animating_images.remove(node);
208 } else {
209 animating_images.maybe_insert_or_update(node, image, self.animation_timeline_value);
210 }
211 }
212
213 pub(crate) fn get_cached_image_for_url(
214 &self,
215 node: OpaqueNode,
216 url: ServoUrl,
217 destination: LayoutImageDestination,
218 is_internal_request: InternalRequest,
219 ) -> Result<CachedImage, ResolveImageError> {
220 if let Some(cached_image) = self.resolved_images_cache.read().get(&url) {
221 return cached_image.clone();
222 }
223
224 let result =
225 self.get_or_request_image_or_meta(node, url.clone(), destination, is_internal_request);
226 match result {
227 LayoutImageCacheResult::DataAvailable(img_or_meta) => match img_or_meta {
228 ImageOrMetadataAvailable::ImageAvailable { image, .. } => {
229 if let Some(image) = image.as_raster_image() {
230 self.handle_animated_image(node, image);
231 }
232
233 let mut resolved_images_cache = self.resolved_images_cache.write();
234 resolved_images_cache.insert(url, Ok(image.clone()));
235 Ok(image)
236 },
237 ImageOrMetadataAvailable::MetadataAvailable(..) => {
238 Result::Err(ResolveImageError::OnlyMetadata)
239 },
240 },
241 LayoutImageCacheResult::Pending => Result::Err(ResolveImageError::ImagePending),
242 LayoutImageCacheResult::LoadError => {
243 let error = Err(ResolveImageError::LoadError);
244 self.resolved_images_cache
245 .write()
246 .insert(url, error.clone());
247 error
248 },
249 }
250 }
251
252 pub(crate) fn rasterize_vector_image(
253 &self,
254 image_id: PendingImageId,
255 size: DeviceIntSize,
256 node: OpaqueNode,
257 svg_id: Option<String>,
258 ) -> Option<RasterImage> {
259 let result = self
260 .image_cache
261 .rasterize_vector_image(image_id, size, svg_id);
262 if result.is_none() {
263 self.pending_rasterization_images
264 .lock()
265 .push(PendingRasterizationImage {
266 id: image_id,
267 node: node.into(),
268 size,
269 });
270 }
271 result
272 }
273
274 pub(crate) fn queue_svg_element_for_serialization(&self, element: ServoLayoutNode<'_>) {
275 self.pending_svg_elements_for_serialization
276 .lock()
277 .push(element.opaque().into())
278 }
279
280 pub(crate) fn resolve_image<'a>(
281 &self,
282 node: Option<OpaqueNode>,
283 image: &'a Image,
284 ) -> Result<ResolvedImage<'a>, ResolveImageError> {
285 match image {
286 Image::None => Result::Err(ResolveImageError::None),
288 Image::CrossFade(_) => Result::Err(ResolveImageError::NotImplementedYet),
289 Image::PaintWorklet(_) => Result::Err(ResolveImageError::NotImplementedYet),
290 Image::Gradient(gradient) => Ok(ResolvedImage::Gradient(gradient)),
291 Image::Image(color) => Ok(ResolvedImage::Color(color)),
292 Image::Url(image_url) => {
293 let image_url = image_url.url().ok_or(ResolveImageError::InvalidUrl)?;
300 let node = node.ok_or(ResolveImageError::MissingNode)?;
301 let image = self.get_cached_image_for_url(
302 node,
303 image_url.clone().into(),
304 LayoutImageDestination::DisplayListBuilding,
305 InternalRequest::No,
306 )?;
307 let metadata = image.metadata();
308 let size = Size2D::new(metadata.width, metadata.height).to_f32();
309 Ok(ResolvedImage::Image { image, size })
310 },
311 Image::ImageSet(image_set) => {
312 image_set
313 .items
314 .get(image_set.selected_index)
315 .ok_or(ResolveImageError::ImageMissingFromImageSet)
316 .and_then(|image| {
317 self.resolve_image(node, &image.image)
318 .map(|info| match info {
319 ResolvedImage::Image {
320 image: cached_image,
321 ..
322 } => {
323 let image_metadata = cached_image.metadata();
330 let size = if cached_image.as_raster_image().is_some() {
331 let scale_factor = image.resolution.dppx();
332 Size2D::new(
333 image_metadata.width as f32 / scale_factor,
334 image_metadata.height as f32 / scale_factor,
335 )
336 } else {
337 Size2D::new(image_metadata.width, image_metadata.height)
338 .to_f32()
339 };
340
341 ResolvedImage::Image {
342 image: cached_image,
343 size,
344 }
345 },
346 _ => info,
347 })
348 })
349 },
350 Image::LightDark(..) => unreachable!("light-dark() should be disabled"),
351 }
352 }
353}