diff --git a/components/layout/context.rs b/components/layout/context.rs index 71372ffe224..62f8a8cdae9 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -61,6 +61,20 @@ impl Drop for LayoutContext<'_> { } } +#[derive(Debug)] +pub enum ResolveImageError { + LoadError, + ImagePending, + ImageRequested, + OnlyMetadata, + InvalidUrl, + MissingNode, + ImageMissingFromImageSet, + FailedToResolveImageFromImageSet, + NotImplementedYet(&'static str), + None, +} + impl LayoutContext<'_> { #[inline(always)] pub fn shared_context(&self) -> &SharedStyleContext { @@ -72,7 +86,7 @@ impl LayoutContext<'_> { node: OpaqueNode, url: ServoUrl, use_placeholder: UsePlaceholder, - ) -> Option { + ) -> Result { // Check for available image or start tracking. let cache_result = self.image_cache.get_cached_image_status( url.clone(), @@ -82,7 +96,7 @@ impl LayoutContext<'_> { ); match cache_result { - ImageCacheResult::Available(img_or_meta) => Some(img_or_meta), + ImageCacheResult::Available(img_or_meta) => Ok(img_or_meta), // Image has been requested, is still pending. Return no image for this paint loop. // When the image loads it will trigger a reflow and/or repaint. ImageCacheResult::Pending(id) => { @@ -93,7 +107,7 @@ impl LayoutContext<'_> { origin: self.origin.clone(), }; self.pending_images.lock().push(image); - None + Result::Err(ResolveImageError::ImagePending) }, // Not yet requested - request image or metadata from the cache ImageCacheResult::ReadyForRequest(id) => { @@ -104,10 +118,10 @@ impl LayoutContext<'_> { origin: self.origin.clone(), }; self.pending_images.lock().push(image); - None + Result::Err(ResolveImageError::ImageRequested) }, // Image failed to load, so just return nothing - ImageCacheResult::LoadError => None, + ImageCacheResult::LoadError => Result::Err(ResolveImageError::LoadError), } } @@ -136,31 +150,34 @@ impl LayoutContext<'_> { node: OpaqueNode, url: ServoUrl, use_placeholder: UsePlaceholder, - ) -> Option { + ) -> Result { if let Some(existing_webrender_image) = self .webrender_image_cache .read() .get(&(url.clone(), use_placeholder)) { - return Some(*existing_webrender_image); + return Ok(*existing_webrender_image); } - - match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) { - Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => { + let image_or_meta = + self.get_or_request_image_or_meta(node, url.clone(), use_placeholder)?; + match image_or_meta { + ImageOrMetadataAvailable::ImageAvailable { image, .. } => { self.handle_animated_image(node, image.clone()); let image_info = WebRenderImageInfo { size: Size2D::new(image.width, image.height), key: image.id, }; if image_info.key.is_none() { - Some(image_info) + Ok(image_info) } else { let mut webrender_image_cache = self.webrender_image_cache.write(); webrender_image_cache.insert((url, use_placeholder), image_info); - Some(image_info) + Ok(image_info) } }, - None | Some(ImageOrMetadataAvailable::MetadataAvailable(..)) => None, + ImageOrMetadataAvailable::MetadataAvailable(..) => { + Result::Err(ResolveImageError::OnlyMetadata) + }, } } @@ -168,11 +185,15 @@ impl LayoutContext<'_> { &self, node: Option, image: &'a Image, - ) -> Option> { + ) -> Result, ResolveImageError> { match image { // TODO: Add support for PaintWorklet and CrossFade rendering. - Image::None | Image::CrossFade(_) | Image::PaintWorklet(_) => None, - Image::Gradient(gradient) => Some(ResolvedImage::Gradient(gradient)), + Image::None => Result::Err(ResolveImageError::None), + Image::CrossFade(_) => Result::Err(ResolveImageError::NotImplementedYet("CrossFade")), + Image::PaintWorklet(_) => { + Result::Err(ResolveImageError::NotImplementedYet("PaintWorklet")) + }, + Image::Gradient(gradient) => Ok(ResolvedImage::Gradient(gradient)), Image::Url(image_url) => { // FIXME: images won’t always have in intrinsic width or // height when support for SVG is added, or a WebRender @@ -180,18 +201,20 @@ impl LayoutContext<'_> { // // FIXME: It feels like this should take into account the pseudo // element and not just the node. - let image_url = image_url.url()?; + let image_url = image_url.url().ok_or(ResolveImageError::InvalidUrl)?; + let node = node.ok_or(ResolveImageError::MissingNode)?; let webrender_info = self.get_webrender_image_for_url( - node?, + node, image_url.clone().into(), UsePlaceholder::No, )?; - Some(ResolvedImage::Image(webrender_info)) + Ok(ResolvedImage::Image(webrender_info)) }, Image::ImageSet(image_set) => { image_set .items .get(image_set.selected_index) + .ok_or(ResolveImageError::ImageMissingFromImageSet) .and_then(|image| { self.resolve_image(node, &image.image) .map(|info| match info { diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs index fa313b306f4..912c69366bd 100644 --- a/components/layout/display_list/mod.rs +++ b/components/layout/display_list/mod.rs @@ -838,8 +838,8 @@ impl<'a> BuilderForBoxFragment<'a> { // Reverse because the property is top layer first, we want to paint bottom layer first. for (index, image) in b.background_image.0.iter().enumerate().rev() { match builder.context.resolve_image(node, image) { - None => {}, - Some(ResolvedImage::Gradient(gradient)) => { + Err(_) => {}, + Ok(ResolvedImage::Gradient(gradient)) => { let intrinsic = NaturalSizes::empty(); let Some(layer) = &background::layout_layer(self, painter, builder, index, intrinsic) @@ -875,7 +875,7 @@ impl<'a> BuilderForBoxFragment<'a> { }, } }, - Some(ResolvedImage::Image(image_info)) => { + Ok(ResolvedImage::Image(image_info)) => { // FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution let dppx = 1.0; let intrinsic = NaturalSizes::from_width_and_height( @@ -1063,8 +1063,8 @@ impl<'a> BuilderForBoxFragment<'a> { .context .resolve_image(node, &border.border_image_source) { - None => return false, - Some(ResolvedImage::Image(image_info)) => { + Err(_) => return false, + Ok(ResolvedImage::Image(image_info)) => { let Some(key) = image_info.key else { return false; }; @@ -1073,7 +1073,7 @@ impl<'a> BuilderForBoxFragment<'a> { height = image_info.size.height as f32; NinePatchBorderSource::Image(key, ImageRendering::Auto) }, - Some(ResolvedImage::Gradient(gradient)) => { + Ok(ResolvedImage::Gradient(gradient)) => { match gradient::build(&self.fragment.style, gradient, border_image_size, builder) { WebRenderGradient::Linear(gradient) => { NinePatchBorderSource::Gradient(gradient) diff --git a/components/layout/replaced.rs b/components/layout/replaced.rs index 6a6b1979ff9..b82fb947074 100644 --- a/components/layout/replaced.rs +++ b/components/layout/replaced.rs @@ -220,13 +220,13 @@ impl ReplacedContents { image_url.clone().into(), UsePlaceholder::No, ) { - Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => { + Ok(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => { (Some(image.clone()), image.width as f32, image.height as f32) }, - Some(ImageOrMetadataAvailable::MetadataAvailable(metadata, _id)) => { + Ok(ImageOrMetadataAvailable::MetadataAvailable(metadata, _id)) => { (None, metadata.width as f32, metadata.height as f32) }, - None => return None, + Err(_) => return None, }; return Some(Self {