LibWeb: Register DecodedImageData consumers as clients
In a future commit, ownership of animation will be transferred from these clients to `AnimatedDecodedImageData` and we will need a way to invalidate them for new frames. This also revealed some `ImageProvider`s which don't yet support animated images (e.g. `<input type="file">`, `<object>`, etc) but that is left as a FIXME for now.
This commit is contained in:
parent
74e04ed258
commit
a7881ca3eb
10 changed files with 100 additions and 15 deletions
|
|
@ -43,6 +43,8 @@ ImageStyleValueResource::ImageStyleValueResource(GC::Ref<HTML::SharedResourceReq
|
|||
ImageStyleValueResource::~ImageStyleValueResource()
|
||||
{
|
||||
stop_animation_timer();
|
||||
VERIFY(m_image_style_values.is_empty());
|
||||
unregister_with_decoded_image_data_if_needed();
|
||||
}
|
||||
|
||||
void ImageStyleValueResource::visit_edges(JS::Cell::Visitor& visitor)
|
||||
|
|
@ -55,23 +57,26 @@ void ImageStyleValueResource::register_image_style_value(DOM::Document& document
|
|||
{
|
||||
m_image_style_values.set(&image_style_value);
|
||||
start_animation_timer_if_needed(document);
|
||||
register_with_decoded_image_data_if_needed();
|
||||
}
|
||||
|
||||
void ImageStyleValueResource::unregister_image_style_value(ImageStyleValue const& image_style_value)
|
||||
{
|
||||
m_image_style_values.remove(&image_style_value);
|
||||
if (m_image_style_values.is_empty())
|
||||
if (m_image_style_values.is_empty()) {
|
||||
stop_animation_timer();
|
||||
unregister_with_decoded_image_data_if_needed();
|
||||
}
|
||||
}
|
||||
|
||||
GC::Ptr<HTML::DecodedImageData> ImageStyleValueResource::image_data() const
|
||||
GC::Ptr<HTML::DecodedImageData> ImageStyleValueResource::decoded_image_data() const
|
||||
{
|
||||
return m_resource_request->image_data();
|
||||
}
|
||||
|
||||
Optional<Gfx::DecodedImageFrame> 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<HTML::DecodedImageData> ImageStyleValue::image_data(DOM::Document const&
|
|||
// resolved URL.
|
||||
VERIFY(resource);
|
||||
|
||||
return resource->image_data();
|
||||
return resource->decoded_image_data();
|
||||
}
|
||||
|
||||
Optional<Gfx::Color> ImageStyleValue::color_if_single_pixel_bitmap(DOM::Document const& document) const
|
||||
|
|
|
|||
|
|
@ -19,12 +19,13 @@
|
|||
#include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
|
||||
#include <LibWeb/CSS/URL.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/HTML/DecodedImageData.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
class ImageStyleValue;
|
||||
|
||||
class ImageStyleValueResource {
|
||||
class ImageStyleValueResource final : public HTML::DecodedImageData::Client {
|
||||
public:
|
||||
explicit ImageStyleValueResource(GC::Ref<HTML::SharedResourceRequest>, GC::Ref<DOM::Document> 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<HTML::DecodedImageData> image_data() const;
|
||||
[[nodiscard]] virtual GC::Ptr<HTML::DecodedImageData> decoded_image_data() const override;
|
||||
[[nodiscard]] Optional<Gfx::DecodedImageFrame> 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&);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,16 @@ class DecodedImageData : public JS::Cell {
|
|||
GC_CELL(DecodedImageData, JS::Cell);
|
||||
|
||||
public:
|
||||
class Client {
|
||||
public:
|
||||
virtual GC::Ptr<DecodedImageData> 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<Client*> m_clients;
|
||||
bool m_is_cors_cross_origin { false };
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ImageRequest> 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<ImageRequest> 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;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <LibWeb/DOM/DocumentLoadEventDelayer.h>
|
||||
#include <LibWeb/DOM/ViewportClient.h>
|
||||
#include <LibWeb/HTML/CORSSettingAttribute.h>
|
||||
#include <LibWeb/HTML/DecodedImageData.h>
|
||||
#include <LibWeb/HTML/HTMLElement.h>
|
||||
#include <LibWeb/HTML/LazyLoadingElement.h>
|
||||
#include <LibWeb/HTML/SourceSet.h>
|
||||
|
|
@ -25,7 +26,8 @@ class HTMLImageElement final
|
|||
: public HTMLElement
|
||||
, public LazyLoadingElement<HTMLImageElement>
|
||||
, 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<ImageRequest>, 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();
|
||||
|
|
|
|||
|
|
@ -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() { }
|
||||
|
|
|
|||
|
|
@ -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<String> 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <LibGC/Ptr.h>
|
||||
#include <LibWeb/DOM/DocumentLoadEventDelayer.h>
|
||||
#include <LibWeb/HTML/DecodedImageData.h>
|
||||
#include <LibWeb/Layout/ImageProvider.h>
|
||||
#include <LibWeb/SVG/SVGAnimatedLength.h>
|
||||
#include <LibWeb/SVG/SVGGraphicsElement.h>
|
||||
|
|
@ -17,12 +18,15 @@ namespace Web::SVG {
|
|||
class SVGImageElement final
|
||||
: public SVGGraphicsElement
|
||||
, public SVGURIReferenceMixin<SupportsXLinkHref::Yes>
|
||||
, 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<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
||||
|
||||
|
|
@ -48,7 +52,11 @@ protected:
|
|||
void fetch_the_document(URL::URL const& url);
|
||||
|
||||
private:
|
||||
virtual void finalize() override;
|
||||
|
||||
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
|
||||
virtual void decoded_image_data_did_update() override { set_needs_repaint(); }
|
||||
|
||||
void animate();
|
||||
|
||||
GC::Ptr<SVG::SVGAnimatedLength> m_x;
|
||||
|
|
|
|||
Loading…
Reference in a new issue