1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 19:27:44 +00:00

LibWeb: Add Frame::ViewportClient and use it for Layout::ImageBox

Image boxes want to know whether they are inside the visible viewport.
This is used to pause/resume animations, and to update the purgeable
memory volatility state.

Previously we would traverse the entire layout tree on every resize,
calling a helper on each ImageBox. Make those boxes register with the
frame they are interested in instead, saving us all that traversal.

This also makes it easier for other parts of the code to learn about
viewport changes in the future. :^)
This commit is contained in:
Andreas Kling 2021-01-30 12:25:33 +01:00
parent 1c6f278677
commit 9b0ca75f84
6 changed files with 39 additions and 22 deletions

View file

@ -30,6 +30,7 @@
#include <LibGfx/ImageDecoder.h> #include <LibGfx/ImageDecoder.h>
#include <LibGfx/StylePainter.h> #include <LibGfx/StylePainter.h>
#include <LibWeb/Layout/ImageBox.h> #include <LibWeb/Layout/ImageBox.h>
#include <LibWeb/Page/Frame.h>
namespace Web::Layout { namespace Web::Layout {
@ -37,10 +38,12 @@ ImageBox::ImageBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr
: ReplacedBox(document, element, move(style)) : ReplacedBox(document, element, move(style))
, m_image_loader(image_loader) , m_image_loader(image_loader)
{ {
frame().register_viewport_client(*this);
} }
ImageBox::~ImageBox() ImageBox::~ImageBox()
{ {
frame().unregister_viewport_client(*this);
} }
int ImageBox::preferred_width() const int ImageBox::preferred_width() const
@ -127,9 +130,9 @@ bool ImageBox::renders_as_alt_text() const
return false; return false;
} }
void ImageBox::set_visible_in_viewport(Badge<Layout::InitialContainingBlockBox>, bool visible_in_viewport) void ImageBox::frame_did_set_viewport_rect(const Gfx::IntRect& viewport_rect)
{ {
m_image_loader.set_visible_in_viewport(visible_in_viewport); m_image_loader.set_visible_in_viewport(viewport_rect.to<float>().intersects(absolute_rect()));
} }
} }

View file

@ -28,10 +28,13 @@
#include <LibWeb/HTML/HTMLImageElement.h> #include <LibWeb/HTML/HTMLImageElement.h>
#include <LibWeb/Layout/ReplacedBox.h> #include <LibWeb/Layout/ReplacedBox.h>
#include <LibWeb/Page/Frame.h>
namespace Web::Layout { namespace Web::Layout {
class ImageBox : public ReplacedBox { class ImageBox
: public ReplacedBox
, public Frame::ViewportClient {
public: public:
ImageBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>, const ImageLoader&); ImageBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>, const ImageLoader&);
virtual ~ImageBox() override; virtual ~ImageBox() override;
@ -43,9 +46,9 @@ public:
bool renders_as_alt_text() const; bool renders_as_alt_text() const;
void set_visible_in_viewport(Badge<InitialContainingBlockBox>, bool);
private: private:
virtual void frame_did_set_viewport_rect(const Gfx::IntRect&) final;
int preferred_width() const; int preferred_width() const;
int preferred_height() const; int preferred_height() const;

View file

@ -63,15 +63,6 @@ void InitialContainingBlockBox::build_stacking_context_tree()
}); });
} }
void InitialContainingBlockBox::did_set_viewport_rect(Badge<Frame>, const Gfx::IntRect& a_viewport_rect)
{
Gfx::FloatRect viewport_rect(a_viewport_rect.x(), a_viewport_rect.y(), a_viewport_rect.width(), a_viewport_rect.height());
for_each_in_subtree_of_type<ImageBox>([&](auto& layout_image) {
const_cast<ImageBox&>(layout_image).set_visible_in_viewport({}, viewport_rect.intersects(layout_image.absolute_rect()));
return IterationDecision::Continue;
});
}
void InitialContainingBlockBox::paint_all_phases(PaintContext& context) void InitialContainingBlockBox::paint_all_phases(PaintContext& context)
{ {
paint(context, PaintPhase::Background); paint(context, PaintPhase::Background);

View file

@ -47,8 +47,6 @@ public:
void set_selection(const LayoutRange&); void set_selection(const LayoutRange&);
void set_selection_end(const LayoutPosition&); void set_selection_end(const LayoutPosition&);
void did_set_viewport_rect(Badge<Frame>, const Gfx::IntRect&);
void build_stacking_context_tree(); void build_stacking_context_tree();
void recompute_selection_states(); void recompute_selection_states();

View file

@ -110,11 +110,11 @@ void Frame::set_size(const Gfx::IntSize& size)
if (m_size == size) if (m_size == size)
return; return;
m_size = size; m_size = size;
if (m_document) { if (m_document)
m_document->update_layout(); m_document->update_layout();
if (m_document->layout_node())
m_document->layout_node()->did_set_viewport_rect({}, viewport_rect()); for (auto* client : m_viewport_clients)
} client->frame_did_set_viewport_rect(viewport_rect());
} }
void Frame::set_viewport_scroll_offset(const Gfx::IntPoint& offset) void Frame::set_viewport_scroll_offset(const Gfx::IntPoint& offset)
@ -123,8 +123,8 @@ void Frame::set_viewport_scroll_offset(const Gfx::IntPoint& offset)
return; return;
m_viewport_scroll_offset = offset; m_viewport_scroll_offset = offset;
if (m_document && m_document->layout_node()) for (auto* client : m_viewport_clients)
m_document->layout_node()->did_set_viewport_rect({}, viewport_rect()); client->frame_did_set_viewport_rect(viewport_rect());
} }
void Frame::set_needs_display(const Gfx::IntRect& rect) void Frame::set_needs_display(const Gfx::IntRect& rect)
@ -274,4 +274,16 @@ String Frame::selected_text() const
return builder.to_string(); return builder.to_string();
} }
void Frame::register_viewport_client(ViewportClient& client)
{
auto result = m_viewport_clients.set(&client);
ASSERT(result == AK::HashSetResult::InsertedNewEntry);
}
void Frame::unregister_viewport_client(ViewportClient& client)
{
bool was_removed = m_viewport_clients.remove(&client);
ASSERT(was_removed);
}
} }

View file

@ -47,6 +47,14 @@ public:
static NonnullRefPtr<Frame> create(Page& page) { return adopt(*new Frame(page)); } static NonnullRefPtr<Frame> create(Page& page) { return adopt(*new Frame(page)); }
~Frame(); ~Frame();
class ViewportClient {
public:
virtual ~ViewportClient() { }
virtual void frame_did_set_viewport_rect(const Gfx::IntRect&) = 0;
};
void register_viewport_client(ViewportClient&);
void unregister_viewport_client(ViewportClient&);
bool is_main_frame() const { return this == &m_main_frame; } bool is_main_frame() const { return this == &m_main_frame; }
bool is_focused_frame() const; bool is_focused_frame() const;
@ -116,6 +124,8 @@ private:
DOM::Position m_cursor_position; DOM::Position m_cursor_position;
RefPtr<Core::Timer> m_cursor_blink_timer; RefPtr<Core::Timer> m_cursor_blink_timer;
bool m_cursor_blink_state { false }; bool m_cursor_blink_state { false };
HashTable<ViewportClient*> m_viewport_clients;
}; };
} }