LibWeb: Notify ImageStyleValue::Clients on animation

Previously image animations driven by `ImageStyleValueResource`
invalidated clients using `on_animate`, this was only implemented by the
`background` presentational attribute of `HTMLBodyElement`.

It now uses `notify_clients_did_update` which is implemented by all
clients.
This commit is contained in:
Callum Law 2026-06-13 14:14:29 +12:00 committed by Alexander Kalenik
parent ce7a963344
commit 11ab66c752
6 changed files with 53 additions and 20 deletions

View file

@ -97,18 +97,22 @@ void ImageStyleValueResource::add_callbacks_if_needed(DOM::Document& document)
[weak_document = GC::Weak(document), url = m_url] {
if (auto document = weak_document.ptr()) {
if (auto* resource = document->css_image_resource(url))
resource->notify_image_style_values_did_update(*document);
resource->on_decoded_image_data_loaded(*document);
}
},
nullptr);
}
void ImageStyleValueResource::notify_image_style_values_did_update(DOM::Document& document)
void ImageStyleValueResource::on_decoded_image_data_loaded(DOM::Document& document)
{
notify_image_style_values_did_update();
start_animation_timer_if_needed(document);
}
void ImageStyleValueResource::notify_image_style_values_did_update()
{
for (auto const* image_style_value : m_image_style_values)
image_style_value->notify_clients_did_update();
start_animation_timer_if_needed(document);
}
void ImageStyleValueResource::start_animation_timer_if_needed(DOM::Document& document)
@ -183,8 +187,7 @@ void ImageStyleValueResource::animate(DOM::Document&)
stop_animation_timer();
}
for (auto const* image_style_value : m_image_style_values)
image_style_value->notify_did_animate();
notify_image_style_values_did_update();
}
ValueComparingNonnullRefPtr<ImageStyleValue const> ImageStyleValue::create(URL const& url)
@ -439,12 +442,6 @@ void ImageStyleValue::notify_clients_did_update() const
client->image_style_value_did_update(const_cast<ImageStyleValue&>(*this));
}
void ImageStyleValue::notify_did_animate() const
{
if (on_animate)
on_animate();
}
Optional<::URL::URL> ImageStyleValue::resolved_url(DOM::Document const& document) const
{
if (m_url.url().is_empty())

View file

@ -45,7 +45,8 @@ public:
private:
void add_callbacks_if_needed(DOM::Document&);
void notify_image_style_values_did_update(DOM::Document&);
void on_decoded_image_data_loaded(DOM::Document&);
void notify_image_style_values_did_update();
void start_animation_timer_if_needed(DOM::Document&);
void stop_animation_timer();
bool is_animatable() const;
@ -109,8 +110,6 @@ public:
Optional<Gfx::DecodedImageFrame> current_frame(DOM::Document const&, DevicePixelRect const& dest_rect) const;
size_t current_frame_index(DOM::Document const&) const;
mutable Function<void()> on_animate;
GC::Ptr<HTML::DecodedImageData> image_data(DOM::Document const&) const;
private:
@ -122,7 +121,6 @@ private:
void register_client(Client&) const;
void unregister_client(Client&) const;
void notify_clients_did_update() const;
void notify_did_animate() const;
void update_style_sheet_resource_context(CSSStyleSheet const&);
Optional<::URL::URL> resolved_url(DOM::Document const&) const;
::URL::URL style_resource_base_url(DOM::Document const&) const;

View file

@ -137,10 +137,6 @@ void HTMLBodyElement::attribute_changed(FlyString const& name, Optional<String>
m_background_style_value = nullptr;
if (auto maybe_background_url = document().encoding_parse_url(value.value_or(String {})); maybe_background_url.has_value()) {
m_background_style_value = CSS::ImageStyleValue::create(maybe_background_url.value());
m_background_style_value->on_animate = [this] {
if (paintable())
paintable()->set_needs_repaint();
};
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

View file

@ -0,0 +1,42 @@
<!doctype html>
<html class="reftest-wait">
<style>
body {
margin: 0;
}
#target {
width: 100px;
height: 50px;
/* red-then-green.gif is 100x50: frame 0 is solid red for 500ms, frame 1 is solid green for 100s, it does not loop */
background: url("../data/red-then-green.gif") no-repeat;
}
</style>
<div id="target"></div>
<script>
const waitForAnimationToStart = () =>
new Promise(resolve => {
const wait = () => {
if (internals.activeImageStyleValueAnimationCount() > 0) {
resolve();
return;
}
requestAnimationFrame(wait);
};
wait();
});
(async () => {
// Wait for the animation to start
await waitForAnimationToStart();
// Wait until the next frame
await new Promise(resolve => setTimeout(resolve, 600));
// Take a screenshot - the image should have been invalidated and repainted.
document.documentElement.classList.remove("reftest-wait");
})();
</script>
</html>