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