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