diff --git a/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp index c23085c140..ebb71799ee 100644 --- a/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp @@ -43,6 +43,8 @@ ImageStyleValueResource::ImageStyleValueResource(GC::Ref ImageStyleValueResource::image_data() const +GC::Ptr ImageStyleValueResource::decoded_image_data() const { return m_resource_request->image_data(); } Optional ImageStyleValueResource::frame(size_t frame_index, Gfx::IntSize size) const { - if (auto image_data = this->image_data()) + if (auto image_data = this->decoded_image_data()) return image_data->frame(frame_index, size); return {}; } @@ -85,6 +90,8 @@ void ImageStyleValueResource::on_decoded_image_data_loaded(DOM::Document& docume { notify_image_style_values_did_update(); start_animation_timer_if_needed(document); + if (!m_image_style_values.is_empty()) + register_with_decoded_image_data_if_needed(); } void ImageStyleValueResource::notify_image_style_values_did_update() @@ -122,7 +129,7 @@ void ImageStyleValueResource::stop_animation_timer() bool ImageStyleValueResource::is_animatable() const { - auto image_data = this->image_data(); + auto image_data = this->decoded_image_data(); if (!image_data || !image_data->is_animated() || image_data->frame_count() <= 1) return false; @@ -131,13 +138,13 @@ bool ImageStyleValueResource::is_animatable() const bool ImageStyleValueResource::animation_has_completed() const { - auto image_data = this->image_data(); + auto image_data = this->decoded_image_data(); return image_data && image_data->loop_count() > 0 && m_loops_completed == image_data->loop_count(); } int ImageStyleValueResource::current_frame_duration() const { - auto image_data = this->image_data(); + auto image_data = this->decoded_image_data(); if (!image_data) return 0; @@ -289,7 +296,7 @@ GC::Ptr ImageStyleValue::image_data(DOM::Document const& // resolved URL. VERIFY(resource); - return resource->image_data(); + return resource->decoded_image_data(); } Optional ImageStyleValue::color_if_single_pixel_bitmap(DOM::Document const& document) const diff --git a/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h index 3815136446..c47b968b60 100644 --- a/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h @@ -19,12 +19,13 @@ #include #include #include +#include namespace Web::CSS { class ImageStyleValue; -class ImageStyleValueResource { +class ImageStyleValueResource final : public HTML::DecodedImageData::Client { public: explicit ImageStyleValueResource(GC::Ref, GC::Ref const&); ~ImageStyleValueResource(); @@ -35,7 +36,7 @@ public: void unregister_image_style_value(ImageStyleValue const&); bool can_be_removed() const { return m_image_style_values.is_empty(); } - [[nodiscard]] GC::Ptr image_data() const; + [[nodiscard]] virtual GC::Ptr decoded_image_data() const override; [[nodiscard]] Optional frame(size_t frame_index, Gfx::IntSize = {}) const; [[nodiscard]] size_t current_frame_index() const { return m_current_frame_index; } [[nodiscard]] bool has_active_animation_timer() const; @@ -43,6 +44,8 @@ public: void animate(DOM::Document&); private: + virtual void decoded_image_data_did_update() override { notify_image_style_values_did_update(); } + void on_decoded_image_data_loaded(DOM::Document&); void notify_image_style_values_did_update(); void start_animation_timer_if_needed(DOM::Document&); diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index aa793d69d4..1223c86bfe 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -6821,7 +6821,7 @@ void Document::prune_image_resource_caches() auto is_used_by_css_image_resource = [&](URL::URL const& url, HTML::SharedResourceRequest const& request) { auto* css_image_resource = this->css_image_resource(url); - return css_image_resource && css_image_resource->image_data() == request.image_data(); + return css_image_resource && css_image_resource->decoded_image_data() == request.image_data(); }; struct CacheSize { diff --git a/Libraries/LibWeb/HTML/DecodedImageData.cpp b/Libraries/LibWeb/HTML/DecodedImageData.cpp index 8608a09542..de7f44e13b 100644 --- a/Libraries/LibWeb/HTML/DecodedImageData.cpp +++ b/Libraries/LibWeb/HTML/DecodedImageData.cpp @@ -8,8 +8,34 @@ namespace Web::HTML { +void DecodedImageData::Client::register_with_decoded_image_data_if_needed() +{ + auto const& image_data = decoded_image_data(); + + if (!image_data) + return; + + image_data->m_clients.set(this); +} + +void DecodedImageData::Client::unregister_with_decoded_image_data_if_needed() +{ + auto const& image_data = decoded_image_data(); + + if (!image_data) + return; + + image_data->m_clients.remove(this); +} + DecodedImageData::DecodedImageData() = default; DecodedImageData::~DecodedImageData() = default; +void DecodedImageData::notify_clients_did_update() +{ + for (auto* client : m_clients) + client->decoded_image_data_did_update(); +} + } diff --git a/Libraries/LibWeb/HTML/DecodedImageData.h b/Libraries/LibWeb/HTML/DecodedImageData.h index 0c01dddb5a..a4cdbbde2e 100644 --- a/Libraries/LibWeb/HTML/DecodedImageData.h +++ b/Libraries/LibWeb/HTML/DecodedImageData.h @@ -22,6 +22,16 @@ class DecodedImageData : public JS::Cell { GC_CELL(DecodedImageData, JS::Cell); public: + class Client { + public: + virtual GC::Ptr decoded_image_data() const = 0; + virtual void decoded_image_data_did_update() = 0; + + protected: + void register_with_decoded_image_data_if_needed(); + void unregister_with_decoded_image_data_if_needed(); + }; + virtual ~DecodedImageData(); [[nodiscard]] bool is_cors_cross_origin() const { return m_is_cors_cross_origin; } @@ -46,7 +56,10 @@ public: protected: DecodedImageData(); + void notify_clients_did_update(); + private: + HashTable m_clients; bool m_is_cors_cross_origin { false }; }; diff --git a/Libraries/LibWeb/HTML/HTMLImageElement.cpp b/Libraries/LibWeb/HTML/HTMLImageElement.cpp index 1a3e3eec5b..86a45b2d4f 100644 --- a/Libraries/LibWeb/HTML/HTMLImageElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLImageElement.cpp @@ -159,6 +159,7 @@ HTMLImageElement::~HTMLImageElement() = default; void HTMLImageElement::finalize() { Base::finalize(); + unregister_with_decoded_image_data_if_needed(); document().unregister_viewport_client(*this); } @@ -687,6 +688,7 @@ void HTMLImageElement::update_the_image_data_impl(bool restart_animations, bool entry->ignore_higher_layer_caching = true; // 2. Abort the image request for the current request and the pending request. + unregister_with_decoded_image_data_if_needed(); abort_the_image_request(realm(), m_current_request); abort_the_image_request(realm(), m_pending_request); @@ -698,6 +700,7 @@ void HTMLImageElement::update_the_image_data_impl(bool restart_animations, bool m_current_request->set_image_data(entry->image_data); m_current_request->set_state(ImageRequest::State::CompletelyAvailable); m_current_frame_index = 0; + register_with_decoded_image_data_if_needed(); // 5. Prepare the current request for presentation given the img element. m_current_request->prepare_for_presentation(*this); @@ -765,6 +768,7 @@ after_step_7: // abort the image request for the current request and the pending request, // and set the pending request to null. m_current_request->set_state(ImageRequest::State::Broken); + unregister_with_decoded_image_data_if_needed(); abort_the_image_request(realm(), m_current_request); abort_the_image_request(realm(), m_pending_request); m_pending_request = nullptr; @@ -801,6 +805,7 @@ after_step_7: // 13. If urlString is failure, then: if (!url_string.has_value()) { // 1. Abort the image request for the current request and the pending request. + unregister_with_decoded_image_data_if_needed(); abort_the_image_request(realm(), m_current_request); abort_the_image_request(realm(), m_pending_request); @@ -865,10 +870,13 @@ after_step_7: // 18. If the current request's state is unavailable or broken, then set the current request to image request. // Otherwise, set the pending request to image request. - if (m_current_request->state() == ImageRequest::State::Unavailable || m_current_request->state() == ImageRequest::State::Broken) + if (m_current_request->state() == ImageRequest::State::Unavailable || m_current_request->state() == ImageRequest::State::Broken) { + unregister_with_decoded_image_data_if_needed(); m_current_request = image_request; - else + register_with_decoded_image_data_if_needed(); + } else { m_pending_request = image_request; + } // 24. Let delay load event be true if the img's lazy loading attribute is in the Eager state, or if scripting is disabled for the img, and false otherwise. auto delay_load_event = lazy_loading_attribute() == LazyLoading::Eager; @@ -955,11 +963,14 @@ void HTMLImageElement::add_callbacks_to_image_request(GC::Ref imag // upgrade the pending request to the current request // and prepare image request for presentation given the img element. if (image_request == m_pending_request) { + unregister_with_decoded_image_data_if_needed(); abort_the_image_request(realm(), m_current_request); upgrade_pending_request_to_current_request(); image_request->prepare_for_presentation(*this); } + register_with_decoded_image_data_if_needed(); + // 2. Set image request to the completely available state. image_request->set_state(ImageRequest::State::CompletelyAvailable); @@ -1005,6 +1016,7 @@ void HTMLImageElement::add_callbacks_to_image_request(GC::Ref imag image_request->set_state(ImageRequest::State::Broken); // abort the image request for the current request and the pending request, + unregister_with_decoded_image_data_if_needed(); abort_the_image_request(realm(), m_current_request); abort_the_image_request(realm(), m_pending_request); @@ -1200,7 +1212,10 @@ void HTMLImageElement::upgrade_pending_request_to_current_request() { // 1. Set the img element's current request to the pending request. VERIFY(m_pending_request); + + unregister_with_decoded_image_data_if_needed(); m_current_request = m_pending_request; + register_with_decoded_image_data_if_needed(); // 2. Set the img element's pending request to null. m_pending_request = nullptr; diff --git a/Libraries/LibWeb/HTML/HTMLImageElement.h b/Libraries/LibWeb/HTML/HTMLImageElement.h index edfa822e3f..792917dca2 100644 --- a/Libraries/LibWeb/HTML/HTMLImageElement.h +++ b/Libraries/LibWeb/HTML/HTMLImageElement.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,8 @@ class HTMLImageElement final : public HTMLElement , public LazyLoadingElement , public Layout::ImageProvider - , public DOM::ViewportClient { + , public DOM::ViewportClient + , public DecodedImageData::Client { WEB_PLATFORM_OBJECT(HTMLImageElement, HTMLElement); GC_DECLARE_ALLOCATOR(HTMLImageElement); LAZY_LOADING_ELEMENT(HTMLImageElement); @@ -136,6 +138,8 @@ private: void handle_failed_fetch(); void add_callbacks_to_image_request(GC::Ref, bool maybe_omit_events, String const& url_string, String const& previous_url, u64 update_the_image_data_count); + virtual void decoded_image_data_did_update() override { set_needs_repaint(); } + bool current_request_has_running_animation() const; void start_animation_timer_if_visible(); void animate(); diff --git a/Libraries/LibWeb/Layout/ImageProvider.h b/Libraries/LibWeb/Layout/ImageProvider.h index faea07eb3b..c985591f65 100644 --- a/Libraries/LibWeb/Layout/ImageProvider.h +++ b/Libraries/LibWeb/Layout/ImageProvider.h @@ -15,6 +15,7 @@ namespace Web::Layout { +// FIXME: Update all ImageProviders to be DecodedImageData::Clients (i.e. support animated images) class ImageProvider { public: virtual ~ImageProvider() { } diff --git a/Libraries/LibWeb/SVG/SVGImageElement.cpp b/Libraries/LibWeb/SVG/SVGImageElement.cpp index 3e2776a36d..20569dc229 100644 --- a/Libraries/LibWeb/SVG/SVGImageElement.cpp +++ b/Libraries/LibWeb/SVG/SVGImageElement.cpp @@ -32,6 +32,12 @@ SVGImageElement::SVGImageElement(DOM::Document& document, DOM::QualifiedName qua SVGImageElement::~SVGImageElement() = default; +void SVGImageElement::finalize() +{ + Base::finalize(); + unregister_with_decoded_image_data_if_needed(); +} + void SVGImageElement::initialize(JS::Realm& realm) { WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGImageElement); @@ -185,6 +191,7 @@ void SVGImageElement::process_the_url(Optional const& href) void SVGImageElement::fetch_the_document(URL::URL const& url) { m_load_event_delayer.emplace(document()); + unregister_with_decoded_image_data_if_needed(); m_resource_request = HTML::SharedResourceRequest::get_or_create(realm(), document().page(), url); m_resource_request->add_callbacks( [this, resource_request = GC::Root { m_resource_request }] { @@ -195,6 +202,7 @@ void SVGImageElement::fetch_the_document(URL::URL const& url) m_animation_timer->set_interval(image_data->frame_duration(0)); m_animation_timer->start(); } + register_with_decoded_image_data_if_needed(); set_needs_style_update(true); set_needs_layout_update(DOM::SetNeedsLayoutReason::SVGImageElementFetchTheDocument); diff --git a/Libraries/LibWeb/SVG/SVGImageElement.h b/Libraries/LibWeb/SVG/SVGImageElement.h index 502ce500ef..63ad5b031d 100644 --- a/Libraries/LibWeb/SVG/SVGImageElement.h +++ b/Libraries/LibWeb/SVG/SVGImageElement.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -17,12 +18,15 @@ namespace Web::SVG { class SVGImageElement final : public SVGGraphicsElement , public SVGURIReferenceMixin - , public Layout::ImageProvider { + , public Layout::ImageProvider + , public HTML::DecodedImageData::Client { WEB_PLATFORM_OBJECT(SVGImageElement, SVGGraphicsElement); GC_DECLARE_ALLOCATOR(SVGImageElement); public: - ~SVGImageElement(); + static constexpr bool OVERRIDES_FINALIZE = true; + + virtual ~SVGImageElement() override; virtual void attribute_changed(FlyString const& name, Optional const& old_value, Optional const& value, Optional const& namespace_) override; @@ -48,7 +52,11 @@ protected: void fetch_the_document(URL::URL const& url); private: + virtual void finalize() override; + virtual RefPtr create_layout_node(CSS::ComputedProperties const&) override; + virtual void decoded_image_data_did_update() override { set_needs_repaint(); } + void animate(); GC::Ptr m_x;