script: Properly root nodes with animating images (#37689)

This change fixes an issue and makes a few more minor improvements to
the `ImageAnimationState`:

1. Image rooting and unrooted now happens in one step from
   `Window::update_animations_post_reflow`.
2. The `node_to_animating_image_map` is now stored as a shared `RwLock`
   so that it doesn't need to be taken and then replaced in the
`ImageAnimationState` during reflow. This should prevent a hypothetical
issue
   where image animations are restarted during empty reflows.
3. General naming and idiomatic Rust usage improvements.

Testing: This doesn't really have any obvious behavioral changes,
because all
reflows currently trigger a restyle. It becomes a serious problem with
#37677
and this change fixes the failing test there.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-06-25 15:52:11 +02:00 committed by GitHub
parent b89a44c539
commit a66a257b38
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 107 additions and 118 deletions

View file

@ -55,7 +55,10 @@ pub struct LayoutContext<'a> {
pub resolved_images_cache:
Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), CachedImageOrError>>>,
pub node_image_animation_map: Arc<RwLock<FxHashMap<OpaqueNode, ImageAnimationState>>>,
/// A shared reference to script's map of DOM nodes with animated images. This is used
/// to manage image animations in script and inform the script about newly animating
/// nodes.
pub node_to_animating_image_map: Arc<RwLock<FxHashMap<OpaqueNode, ImageAnimationState>>>,
/// The DOM node that is highlighted by the devtools inspector, if any
pub highlighted_dom_node: Option<OpaqueNode>,
@ -153,31 +156,24 @@ impl LayoutContext<'_> {
}
pub fn handle_animated_image(&self, node: OpaqueNode, image: Arc<RasterImage>) {
let mut store = self.node_image_animation_map.write();
let mut map = self.node_to_animating_image_map.write();
if !image.should_animate() {
map.remove(&node);
return;
}
let new_image_animation_state = || {
ImageAnimationState::new(
image.clone(),
self.shared_context().current_time_for_animations,
)
};
// 1. first check whether node previously being track for animated image.
if let Some(image_state) = store.get(&node) {
// a. if the node is not containing the same image as before.
if image_state.image_key() != image.id {
if image.should_animate() {
// i. Register/Replace tracking item in image_animation_manager.
store.insert(
node,
ImageAnimationState::new(
image,
self.shared_context().current_time_for_animations,
),
);
} else {
// ii. Cancel Action if the node's image is no longer animated.
store.remove(&node);
}
}
} else if image.should_animate() {
store.insert(
node,
ImageAnimationState::new(image, self.shared_context().current_time_for_animations),
);
let entry = map.entry(node).or_insert_with(new_image_animation_state);
// If the entry exists, but it is for a different image id, replace it as the image
// has changed during this layout.
if entry.image.id != image.id {
*entry = new_image_animation_state();
}
}