diff --git a/components/layout/context.rs b/components/layout/context.rs index 1008dbc927a..3e594ed331b 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -10,7 +10,8 @@ use fnv::FnvHashMap; use fonts::FontContext; use layout_api::wrapper_traits::ThreadSafeLayoutNode; use layout_api::{ - IFrameSizes, ImageAnimationState, PendingImage, PendingImageState, PendingRasterizationImage, + IFrameSizes, ImageAnimationState, LayoutImageDestination, PendingImage, PendingImageState, + PendingRasterizationImage, }; use net_traits::image_cache::{ Image as CachedImage, ImageCache, ImageCacheResult, ImageOrMetadataAvailable, PendingImageId, @@ -128,6 +129,7 @@ impl ImageResolver { node: OpaqueNode, url: ServoUrl, use_placeholder: UsePlaceholder, + destination: LayoutImageDestination, ) -> LayoutImageCacheResult { // Check for available image or start tracking. let cache_result = self.image_cache.get_cached_image_status( @@ -149,6 +151,7 @@ impl ImageResolver { node: node.into(), id, origin: self.origin.clone(), + destination, }; self.pending_images.lock().push(image); LayoutImageCacheResult::Pending @@ -160,6 +163,7 @@ impl ImageResolver { node: node.into(), id, origin: self.origin.clone(), + destination, }; self.pending_images.lock().push(image); LayoutImageCacheResult::Pending @@ -192,6 +196,7 @@ impl ImageResolver { node: OpaqueNode, url: ServoUrl, use_placeholder: UsePlaceholder, + destination: LayoutImageDestination, ) -> Result { if let Some(cached_image) = self .resolved_images_cache @@ -201,7 +206,8 @@ impl ImageResolver { return cached_image.clone(); } - let result = self.get_or_request_image_or_meta(node, url.clone(), use_placeholder); + let result = + self.get_or_request_image_or_meta(node, url.clone(), use_placeholder, destination); match result { LayoutImageCacheResult::DataAvailable(img_or_meta) => match img_or_meta { ImageOrMetadataAvailable::ImageAvailable { image, .. } => { @@ -280,6 +286,7 @@ impl ImageResolver { node, image_url.clone().into(), UsePlaceholder::No, + LayoutImageDestination::DisplayListBuilding, )?; let metadata = image.metadata(); let size = Size2D::new(metadata.width, metadata.height).to_f32(); diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs index 5f9359f416d..7a4ddf63f23 100644 --- a/components/layout/layout_impl.rs +++ b/components/layout/layout_impl.rs @@ -539,6 +539,10 @@ impl Layout for LayoutThread { self.need_new_display_list.get() } + fn set_needs_new_display_list(&self) { + self.need_new_display_list.set(true); + } + /// fn register_custom_property( &mut self, diff --git a/components/layout/replaced.rs b/components/layout/replaced.rs index 95c9928b77d..80fce3677c5 100644 --- a/components/layout/replaced.rs +++ b/components/layout/replaced.rs @@ -7,8 +7,8 @@ use base::id::{BrowsingContextId, PipelineId}; use data_url::DataUrl; use embedder_traits::ViewportDetails; use euclid::{Scale, Size2D}; -use layout_api::IFrameSize; use layout_api::wrapper_traits::ThreadSafeLayoutNode; +use layout_api::{IFrameSize, LayoutImageDestination}; use malloc_size_of_derive::MallocSizeOf; use net_traits::image_cache::{Image, ImageOrMetadataAvailable, UsePlaceholder, VectorImage}; use script::layout_dom::ServoThreadSafeLayoutNode; @@ -189,7 +189,12 @@ impl ReplacedContents { let result = context .image_resolver - .get_cached_image_for_url(node.opaque(), svg_source, UsePlaceholder::No) + .get_cached_image_for_url( + node.opaque(), + svg_source, + UsePlaceholder::No, + LayoutImageDestination::BoxTreeConstruction, + ) .ok(); let vector_image = result.map(|result| match result { @@ -230,6 +235,7 @@ impl ReplacedContents { node.opaque(), image_url.clone().into(), UsePlaceholder::No, + LayoutImageDestination::BoxTreeConstruction, ) { LayoutImageCacheResult::DataAvailable(img_or_meta) => match img_or_meta { ImageOrMetadataAvailable::ImageAvailable { image, .. } => { diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 254eb385acc..5b19243bd29 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -55,9 +55,10 @@ use js::rust::{ }; use layout_api::{ BoxAreaType, ElementsFromPointFlags, ElementsFromPointResult, FragmentType, Layout, - PendingImage, PendingImageState, PendingRasterizationImage, QueryMsg, ReflowGoal, - ReflowPhasesRun, ReflowRequest, ReflowRequestRestyle, RestyleReason, ScrollContainerQueryType, - ScrollContainerResponse, TrustedNodeAddress, combine_id_with_fragment_type, + LayoutImageDestination, PendingImage, PendingImageState, PendingRasterizationImage, QueryMsg, + ReflowGoal, ReflowPhasesRun, ReflowRequest, ReflowRequestRestyle, RestyleReason, + ScrollContainerQueryType, ScrollContainerResponse, TrustedNodeAddress, + combine_id_with_fragment_type, }; use malloc_size_of::MallocSizeOf; use media::WindowGLContext; @@ -235,6 +236,17 @@ pub(crate) struct OngoingNavigation(u32); type PendingImageRasterizationKey = (PendingImageId, DeviceIntSize); +/// Ancillary data of pending image request that was initiated by layout during a reflow. +/// This data is used to faciliate invalidating layout when the image data becomes available +/// at some point in the future. +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +#[derive(JSTraceable, MallocSizeOf)] +struct PendingLayoutImageAncillaryData { + node: Dom, + #[no_trace] + destination: LayoutImageDestination, +} + #[dom_struct] pub(crate) struct Window { globalscope: GlobalScope, @@ -360,7 +372,8 @@ pub(crate) struct Window { /// initiated by layout during a reflow. They are stored in the [`ScriptThread`] /// to ensure that the element can be marked dirty when the image data becomes /// available at some point in the future. - pending_layout_images: DomRefCell>>>, + pending_layout_images: + DomRefCell>>, /// Vector images for which layout has intiated rasterization at a specific size /// and whose results are not yet available. They are stored in the [`ScriptThread`] @@ -643,11 +656,22 @@ impl Window { Entry::Occupied(nodes) => nodes, Entry::Vacant(_) => return, }; - if matches!(response.response, ImageResponse::Loaded(_, _)) { - for node in nodes.get() { - node.dirty(NodeDamage::Other); + if matches!( + response.response, + ImageResponse::Loaded(_, _) | ImageResponse::PlaceholderLoaded(_, _) + ) { + for ancillary_data in nodes.get() { + match ancillary_data.destination { + LayoutImageDestination::BoxTreeConstruction => { + ancillary_data.node.dirty(NodeDamage::Other); + }, + LayoutImageDestination::DisplayListBuilding => { + self.layout().set_needs_new_display_list(); + }, + } } } + match response.response { ImageResponse::MetadataLoaded(_) => {}, ImageResponse::Loaded(_, _) | @@ -3106,8 +3130,11 @@ impl Window { } let nodes = images.entry(id).or_default(); - if !nodes.iter().any(|n| std::ptr::eq(&**n, &*node)) { - nodes.push(Dom::from_ref(&*node)); + if !nodes.iter().any(|n| std::ptr::eq(&*(n.node), &*node)) { + nodes.push(PendingLayoutImageAncillaryData { + node: Dom::from_ref(&*node), + destination: image.destination, + }); } } diff --git a/components/shared/layout/lib.rs b/components/shared/layout/lib.rs index d3ed7fdea32..c44cdea0e5f 100644 --- a/components/shared/layout/lib.rs +++ b/components/shared/layout/lib.rs @@ -154,6 +154,13 @@ pub enum PendingImageState { PendingResponse, } +/// The destination in layout where an image is needed. +#[derive(Debug, MallocSizeOf)] +pub enum LayoutImageDestination { + BoxTreeConstruction, + DisplayListBuilding, +} + /// The data associated with an image that is not yet present in the image cache. /// Used by the script thread to hold on to DOM elements that need to be repainted /// when an image fetch is complete. @@ -163,6 +170,7 @@ pub struct PendingImage { pub node: UntrustedNodeAddress, pub id: PendingImageId, pub origin: ImmutableOrigin, + pub destination: LayoutImageDestination, } /// A data structure to tarck vector image that are fully loaded (i.e has a parsed SVG @@ -290,6 +298,9 @@ pub trait Layout { /// Returns true if this layout needs to produce a new display list for rendering updates. fn needs_new_display_list(&self) -> bool; + /// Marks that this layout needs to produce a new display list for rendering updates. + fn set_needs_new_display_list(&self); + fn query_box_area(&self, node: TrustedNodeAddress, area: BoxAreaType) -> Option>; fn query_box_areas(&self, node: TrustedNodeAddress, area: BoxAreaType) -> Vec>; fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect;