LibWeb: Make Paintable tree ref-counted

The Paintable tree and its supplemental painting data structures were
GC allocated because that was the easiest way to manage it and avoid
leaks introduced by ref cycles. This included the Paintable subclasses
themselves plus StackingContext, ChromeWidget, Scrollbar, ResizeHandle,
and scroll-frame state.

We are now trying to reduce GC allocation churn on layout and painting
updates, so keeping this short-lived rendering tree outside the JS heap
is a better fit. Move Paintable to RefCountedTreeNode, make painting
helpers ref-counted or weakly reference Paintables, and update the
layout and event-handler call sites to use RefPtr/WeakPtr ownership.
This commit is contained in:
Aliaksandr Kalenik 2026-05-07 13:38:05 +02:00 committed by Alexander Kalenik
parent 8bc3f5950f
commit 568b7ce7ea
135 changed files with 682 additions and 698 deletions

View file

@ -145,7 +145,7 @@ void ScrollTimeline::update_current_time(double)
if (!layout_node || !layout_node->is_scroll_container())
return;
auto const& paintable_box = propagated_source.visit([](auto const& source) -> Painting::PaintableBox const* { return source->unsafe_paintable_box(); });
auto paintable_box = propagated_source.visit([](auto const& source) -> RefPtr<Painting::PaintableBox const> { return source->unsafe_paintable_box(); });
if (!paintable_box || !paintable_box->has_scrollable_overflow())
return;

View file

@ -662,10 +662,12 @@ RefPtr<StyleValue const> CSSStyleProperties::style_value_for_computed_property(L
auto used_value_for_property = [&layout_node, property_id](Function<CSSPixels(Painting::PaintableBox const&)>&& used_value_getter) -> Optional<CSSPixels> {
auto const& display = layout_node.computed_values().display();
if (!display.is_none() && !display.is_contents() && layout_node.first_paintable()) {
if (auto const* paintable_box = as_if<Painting::PaintableBox>(layout_node.first_paintable()))
if (!display.is_none() && !display.is_contents()) {
auto first_paintable = layout_node.first_paintable();
if (auto const* paintable_box = as_if<Painting::PaintableBox>(first_paintable.ptr()))
return used_value_getter(*paintable_box);
dbgln("FIXME: Support getting used value for property `{}` on {}", string_from_property_id(property_id), layout_node.debug_description());
if (first_paintable)
dbgln("FIXME: Support getting used value for property `{}` on {}", string_from_property_id(property_id), layout_node.debug_description());
}
return {};
};
@ -889,8 +891,9 @@ RefPtr<StyleValue const> CSSStyleProperties::style_value_for_computed_property(L
auto transform = FloatMatrix4x4::identity();
// 2. Post-multiply all <transform-function>s in <transform-list> to transform.
VERIFY(layout_node.first_paintable());
auto const& paintable_box = as<Painting::PaintableBox const>(*layout_node.first_paintable());
auto first_paintable = layout_node.first_paintable();
VERIFY(first_paintable);
auto const& paintable_box = as<Painting::PaintableBox const>(*first_paintable);
for (auto const& transformation : transformations) {
transform = transform * transformation->to_matrix(paintable_box).release_value();
}
@ -1023,16 +1026,14 @@ RefPtr<StyleValue const> CSSStyleProperties::style_value_for_computed_property(L
// For grid-template-columns and grid-template-rows the resolved value is the used value.
// https://www.w3.org/TR/css-grid-2/#resolved-track-list-standalone
if (property_id == PropertyID::GridTemplateColumns) {
if (layout_node.first_paintable() && layout_node.first_paintable()->is_paintable_box()) {
auto const& paintable_box = as<Painting::PaintableBox const>(*layout_node.first_paintable());
if (auto used_values_for_grid_template_columns = paintable_box.used_values_for_grid_template_columns()) {
if (auto first_paintable = layout_node.first_paintable(); auto const* paintable_box = as_if<Painting::PaintableBox>(first_paintable.ptr())) {
if (auto used_values_for_grid_template_columns = paintable_box->used_values_for_grid_template_columns()) {
return used_values_for_grid_template_columns;
}
}
} else if (property_id == PropertyID::GridTemplateRows) {
if (layout_node.first_paintable() && layout_node.first_paintable()->is_paintable_box()) {
auto const& paintable_box = as<Painting::PaintableBox const>(*layout_node.first_paintable());
if (auto used_values_for_grid_template_rows = paintable_box.used_values_for_grid_template_rows()) {
if (auto first_paintable = layout_node.first_paintable(); auto const* paintable_box = as_if<Painting::PaintableBox>(first_paintable.ptr())) {
if (auto used_values_for_grid_template_rows = paintable_box->used_values_for_grid_template_rows()) {
return used_values_for_grid_template_rows;
}
}

View file

@ -1240,7 +1240,7 @@ RefPtr<StyleValue const> interpolate_transform(DOM::Element& element, Calculatio
generic_function = TransformFunction::Matrix3d;
// NB: Called during animation interpolation.
auto paintable_box = [&] -> Optional<Painting::PaintableBox const&> {
if (auto* box = element.unsafe_paintable_box())
if (auto box = element.unsafe_paintable_box())
return *box;
return {};
}();
@ -1343,8 +1343,9 @@ RefPtr<StyleValue const> interpolate_transform(DOM::Element& element, Calculatio
// iterating over Va and Vb.
// NB: Called during animation interpolation.
Optional<Painting::PaintableBox const&> paintable_box;
if (auto* paintable = as_if<Painting::PaintableBox>(element.unsafe_paintable()))
paintable_box = *paintable;
auto paintable = element.unsafe_paintable();
if (auto const* box = as_if<Painting::PaintableBox>(paintable.ptr()))
paintable_box = *box;
auto post_multiply_remaining_transformations = [&paintable_box](size_t start_index, Vector<NonnullRefPtr<TransformationStyleValue const>> const& transformations) -> Optional<FloatMatrix4x4> {
FloatMatrix4x4 result = FloatMatrix4x4::identity();

View file

@ -1458,13 +1458,13 @@ static void relayout_svg_root(Layout::SVGSVGBox& svg_root)
Layout::LayoutState layout_state(svg_root);
// Pre-populate the svg_root itself.
if (auto const* paintable = svg_root.paintable_box())
if (auto paintable = svg_root.paintable_box())
layout_state.populate_from_paintable(svg_root, *paintable);
// Pre-populate SVGGraphicsBox ancestors (up to outer SVG) for get_parent_svg_transform().
for (auto* ancestor = svg_root.parent(); ancestor; ancestor = ancestor->parent()) {
if (auto const* svg_graphics_ancestor = as_if<Layout::SVGGraphicsBox>(*ancestor)) {
if (auto const* paintable = svg_graphics_ancestor->paintable_box())
if (auto paintable = svg_graphics_ancestor->paintable_box())
layout_state.populate_from_paintable(*svg_graphics_ancestor, *paintable);
}
if (is<Layout::SVGSVGBox>(*ancestor))
@ -1473,7 +1473,7 @@ static void relayout_svg_root(Layout::SVGSVGBox& svg_root)
// Pre-populate the viewport for position:fixed elements inside <foreignObject>.
auto& viewport = svg_root.root();
if (auto const* paintable = viewport.paintable_box())
if (auto paintable = viewport.paintable_box())
layout_state.populate_from_paintable(viewport, *paintable);
auto const& svg_state = layout_state.get(svg_root);
@ -1713,7 +1713,7 @@ void Document::update_layout(UpdateLayoutReason reason)
}
// Collect elements with content-visibility: auto. This is used in the HTML event loop to avoid traversing the whole tree every time.
Vector<GC::Ref<Painting::PaintableBox>> paintable_boxes_with_auto_content_visibility;
Vector<WeakPtr<Painting::PaintableBox>> paintable_boxes_with_auto_content_visibility;
unsafe_paintable()->for_each_in_subtree_of_type<Painting::PaintableBox>([&](auto& paintable_box) {
if (paintable_box.dom_node()
&& paintable_box.dom_node()->is_element()
@ -1964,13 +1964,13 @@ void Document::set_needs_animated_style_update()
void Document::update_paint_and_hit_testing_properties_if_needed()
{
// NB: Called during paint property resolution.
if (auto* paintable = this->unsafe_paintable()) {
if (auto paintable = this->unsafe_paintable()) {
paintable->refresh_scroll_state();
}
if (m_needs_accumulated_visual_contexts_update) {
m_needs_accumulated_visual_contexts_update = false;
if (auto* paintable = this->unsafe_paintable()) {
if (auto paintable = this->unsafe_paintable()) {
paintable->assign_accumulated_visual_contexts();
}
}
@ -4249,7 +4249,7 @@ void Document::set_page_showing(bool page_showing)
void Document::invalidate_stacking_context_tree()
{
// NB: Called during stacking context invalidation.
if (auto* paintable_box = this->unsafe_paintable_box())
if (auto paintable_box = this->unsafe_paintable_box())
paintable_box->invalidate_stacking_context();
}
@ -5381,15 +5381,15 @@ void Document::queue_an_intersection_observer_entry(IntersectionObserver::Inters
}
// https://www.w3.org/TR/intersection-observer/#compute-the-intersection
static CSSPixelRect compute_intersection(GC::Ref<Element> target, CSSPixelRect target_rect, IntersectionObserver::IntersectionObserver const& observer, Painting::PaintableBox const* root_paintable, CSSPixelRect const& root_bounds)
static CSSPixelRect compute_intersection(GC::Ref<Element> target, CSSPixelRect target_rect, IntersectionObserver::IntersectionObserver const& observer, RefPtr<Painting::PaintableBox> root_paintable, CSSPixelRect const& root_bounds)
{
// 1. Let intersectionRect be the result of getting the bounding box for target.
auto intersection_rect = target_rect;
// 2. Let container be the containing block of target.
// 3. While container is not root:
if (auto const* target_paintable = target->paintable_box()) {
for (auto const* container = target_paintable->containing_block(); container; container = container->containing_block()) {
if (auto target_paintable = target->paintable_box()) {
for (auto container = target_paintable->containing_block(); container; container = container->containing_block()) {
// Stop when we reach the intersection root.
if (container == root_paintable)
break;
@ -5460,7 +5460,7 @@ void Document::run_the_update_intersection_observations_steps(HighResolutionTime
// Pre-compute per-observer values to avoid repeated work in the per-target loop.
auto intersection_root_node = observer->intersection_root_node();
auto* root_paintable = intersection_root_node->paintable_box();
auto root_paintable = intersection_root_node->paintable_box();
bool is_implicit_root = observer->is_implicit_root();
bool root_is_element = intersection_root_node->is_element();
@ -5807,24 +5807,36 @@ void Document::shared_declarative_refresh_steps(StringView input, GC::Ptr<HTML::
}
}
Painting::ViewportPaintable const* Document::paintable() const
RefPtr<Painting::ViewportPaintable const> Document::paintable() const
{
return static_cast<Painting::ViewportPaintable const*>(Node::paintable());
auto paintable = Node::paintable();
if (!paintable)
return nullptr;
return as<Painting::ViewportPaintable>(*paintable);
}
Painting::ViewportPaintable* Document::paintable()
RefPtr<Painting::ViewportPaintable> Document::paintable()
{
return static_cast<Painting::ViewportPaintable*>(Node::paintable());
auto paintable = Node::paintable();
if (!paintable)
return nullptr;
return as<Painting::ViewportPaintable>(*paintable);
}
Painting::ViewportPaintable const* Document::unsafe_paintable() const
RefPtr<Painting::ViewportPaintable const> Document::unsafe_paintable() const
{
return static_cast<Painting::ViewportPaintable const*>(Node::unsafe_paintable());
auto paintable = Node::unsafe_paintable();
if (!paintable)
return nullptr;
return as<Painting::ViewportPaintable>(*paintable);
}
Painting::ViewportPaintable* Document::unsafe_paintable()
RefPtr<Painting::ViewportPaintable> Document::unsafe_paintable()
{
return static_cast<Painting::ViewportPaintable*>(Node::unsafe_paintable());
auto paintable = Node::unsafe_paintable();
if (!paintable)
return nullptr;
return as<Painting::ViewportPaintable>(*paintable);
}
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#restore-the-history-object-state
@ -6399,7 +6411,7 @@ Element const* Document::element_from_point(double x, double y)
// 2. If there is a box in the viewport that would be a target for hit testing at coordinates x,y, when applying the transforms
// that apply to the descendants of the viewport, return the associated element and terminate these steps.
GC::Ptr<Element> hit_element;
if (auto const* paintable_box = this->paintable_box()) {
if (auto paintable_box = this->paintable_box()) {
(void)paintable_box->hit_test(position, Painting::HitTestType::Exact, [&](Painting::HitTestResult result) {
if (auto* element = as_if<Element>(result.dom_node())) {
hit_element = element;
@ -6442,7 +6454,7 @@ GC::RootVector<GC::Ref<Element>> Document::elements_from_point(double x, double
// 3. For each box in the viewport, in paint order, starting with the topmost box, that would be a target for
// hit testing at coordinates x,y even if nothing would be overlapping it, when applying the transforms that
// apply to the descendants of the viewport, append the associated element to sequence.
if (auto const* paintable_box = this->paintable_box()) {
if (auto paintable_box = this->paintable_box()) {
(void)paintable_box->hit_test(position, Painting::HitTestType::Exact, [&](Painting::HitTestResult result) {
if (auto* element = as_if<Element>(result.dom_node())) {
// AD-HOC: If element is inside a UA internal shadow root, retarget to the host.
@ -6976,7 +6988,7 @@ GC::Ptr<HTML::HTMLElement> Document::topmost_auto_or_hint_popover()
void Document::set_needs_to_refresh_scroll_state(bool b)
{
// NB: Propagating scroll state invalidation.
if (auto* paintable = this->unsafe_paintable())
if (auto paintable = this->unsafe_paintable())
paintable->set_needs_to_refresh_scroll_state(b);
}
@ -7917,7 +7929,7 @@ String Document::dump_display_list()
{
update_layout(UpdateLayoutReason::DumpDisplayList);
auto* viewport_paintable = paintable();
auto viewport_paintable = paintable();
if (!viewport_paintable)
return "No paintable"_string;
@ -7925,11 +7937,11 @@ String Document::dump_display_list()
if (!display_list)
return "No display list"_string;
HashMap<size_t, Painting::PaintableBox const*> context_id_to_paintable;
HashMap<size_t, RefPtr<Painting::PaintableBox const>> context_id_to_paintable;
viewport_paintable->for_each_in_inclusive_subtree_of_type<Painting::PaintableBox>([&](auto const& paintable_box) {
auto visual_context_index = paintable_box.accumulated_visual_context_index();
if (visual_context_index.value())
(void)context_id_to_paintable.try_set(visual_context_index.value(), &paintable_box);
(void)context_id_to_paintable.try_set(visual_context_index.value(), paintable_box);
return TraversalDecision::Continue;
});
@ -8009,13 +8021,13 @@ String Document::dump_stacking_context_tree()
{
update_layout(UpdateLayoutReason::DumpDisplayList);
auto* viewport_paintable = paintable();
auto viewport_paintable = paintable();
if (!viewport_paintable)
return "No paintable"_string;
viewport_paintable->build_stacking_context_tree_if_needed();
auto* stacking_context = viewport_paintable->stacking_context();
auto stacking_context = viewport_paintable->stacking_context();
if (!stacking_context)
return "No stacking context"_string;

View file

@ -15,6 +15,7 @@
#include <AK/HashTable.h>
#include <AK/Optional.h>
#include <AK/OwnPtr.h>
#include <AK/RefPtr.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <AK/WeakPtr.h>
@ -399,11 +400,11 @@ public:
Layout::Viewport const* unsafe_layout_node() const;
Layout::Viewport* unsafe_layout_node();
Painting::ViewportPaintable const* paintable() const;
Painting::ViewportPaintable* paintable();
RefPtr<Painting::ViewportPaintable const> paintable() const;
RefPtr<Painting::ViewportPaintable> paintable();
Painting::ViewportPaintable const* unsafe_paintable() const;
Painting::ViewportPaintable* unsafe_paintable();
RefPtr<Painting::ViewportPaintable const> unsafe_paintable() const;
RefPtr<Painting::ViewportPaintable> unsafe_paintable();
GC::Ref<NodeList> get_elements_by_name(FlyString const&);

View file

@ -1543,7 +1543,7 @@ static Vector<CSSPixelRect> compute_client_rects_assuming_layout_clean(Element c
// are left in the final list.
Vector<CSSPixelRect> rects;
if (auto const* paintable_box = element.paintable_box()) {
if (auto paintable_box = element.paintable_box()) {
auto absolute_rect = paintable_box->absolute_border_box_rect();
rects.append(paintable_box->transform_rect_to_viewport(absolute_rect));
} else if (element.paintable()) {

View file

@ -133,8 +133,6 @@ void Node::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_child_nodes);
visitor.visit(m_layout_node);
visitor.visit(m_paintable);
if (m_registered_observer_list) {
visitor.visit(*m_registered_observer_list);
}
@ -2656,7 +2654,7 @@ Layout::Node* Node::layout_node()
return m_layout_node;
}
void Node::set_paintable(GC::Ptr<Painting::Paintable> paintable)
void Node::set_paintable(WeakPtr<Painting::Paintable> paintable)
{
m_paintable = paintable;
}
@ -2670,7 +2668,7 @@ void Node::set_needs_repaint(InvalidateDisplayList should_invalidate_display_lis
{
if (auto* layout_node = unsafe_layout_node()) {
for (auto& paintable : layout_node->paintables())
paintable.set_needs_repaint(should_invalidate_display_list);
paintable->set_needs_repaint(should_invalidate_display_list);
}
}
@ -2682,45 +2680,55 @@ void Node::set_needs_layout_update(SetNeedsLayoutReason reason)
}
}
Painting::Paintable const* Node::paintable() const
RefPtr<Painting::Paintable const> Node::paintable() const
{
if (m_paintable)
VERIFY(document().layout_is_up_to_date());
return m_paintable;
return m_paintable.strong_ref();
}
Painting::Paintable* Node::paintable()
RefPtr<Painting::Paintable> Node::paintable()
{
if (m_paintable)
VERIFY(document().layout_is_up_to_date());
return m_paintable;
return m_paintable.strong_ref();
}
Painting::PaintableBox const* Node::paintable_box() const
RefPtr<Painting::Paintable const> Node::unsafe_paintable() const
{
if (auto* p = paintable(); p && p->is_paintable_box())
return static_cast<Painting::PaintableBox const*>(p);
return m_paintable.strong_ref();
}
RefPtr<Painting::Paintable> Node::unsafe_paintable()
{
return m_paintable.strong_ref();
}
RefPtr<Painting::PaintableBox const> Node::paintable_box() const
{
if (auto p = paintable(); p && p->is_paintable_box())
return static_cast<Painting::PaintableBox const&>(*p);
return nullptr;
}
Painting::PaintableBox* Node::paintable_box()
RefPtr<Painting::PaintableBox> Node::paintable_box()
{
if (auto* p = paintable(); p && p->is_paintable_box())
return static_cast<Painting::PaintableBox*>(p);
if (auto p = paintable(); p && p->is_paintable_box())
return static_cast<Painting::PaintableBox&>(*p);
return nullptr;
}
Painting::PaintableBox const* Node::unsafe_paintable_box() const
RefPtr<Painting::PaintableBox const> Node::unsafe_paintable_box() const
{
if (m_paintable && m_paintable->is_paintable_box())
return static_cast<Painting::PaintableBox const*>(m_paintable.ptr());
if (auto paintable = m_paintable.strong_ref(); paintable && paintable->is_paintable_box())
return static_cast<Painting::PaintableBox const&>(*paintable);
return nullptr;
}
Painting::PaintableBox* Node::unsafe_paintable_box()
RefPtr<Painting::PaintableBox> Node::unsafe_paintable_box()
{
if (m_paintable && m_paintable->is_paintable_box())
return static_cast<Painting::PaintableBox*>(m_paintable.ptr());
if (auto paintable = m_paintable.strong_ref(); paintable && paintable->is_paintable_box())
return static_cast<Painting::PaintableBox&>(*paintable);
return nullptr;
}

View file

@ -10,8 +10,10 @@
#include <AK/FlyString.h>
#include <AK/Function.h>
#include <AK/GenericShorthands.h>
#include <AK/RefPtr.h>
#include <AK/TypeCasts.h>
#include <AK/Vector.h>
#include <AK/WeakPtr.h>
#include <LibWeb/CSS/InvalidationSet.h>
#include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/DOM/FragmentSerializationMode.h>
@ -329,17 +331,17 @@ public:
Layout::Node const* unsafe_layout_node() const { return m_layout_node; }
Layout::Node* unsafe_layout_node() { return m_layout_node; }
Painting::PaintableBox const* paintable_box() const;
Painting::PaintableBox* paintable_box();
Painting::Paintable const* paintable() const;
Painting::Paintable* paintable();
RefPtr<Painting::PaintableBox const> paintable_box() const;
RefPtr<Painting::PaintableBox> paintable_box();
RefPtr<Painting::Paintable const> paintable() const;
RefPtr<Painting::Paintable> paintable();
Painting::PaintableBox const* unsafe_paintable_box() const;
Painting::PaintableBox* unsafe_paintable_box();
Painting::Paintable const* unsafe_paintable() const { return m_paintable; }
Painting::Paintable* unsafe_paintable() { return m_paintable; }
RefPtr<Painting::PaintableBox const> unsafe_paintable_box() const;
RefPtr<Painting::PaintableBox> unsafe_paintable_box();
RefPtr<Painting::Paintable const> unsafe_paintable() const;
RefPtr<Painting::Paintable> unsafe_paintable();
void set_paintable(GC::Ptr<Painting::Paintable>);
void set_paintable(WeakPtr<Painting::Paintable>);
void clear_paintable();
void set_needs_repaint(InvalidateDisplayList = InvalidateDisplayList::Yes);
@ -498,7 +500,7 @@ protected:
GC::Ptr<Document> m_document;
GC::Ptr<Layout::Node> m_layout_node;
GC::Ptr<Painting::Paintable> m_paintable;
WeakPtr<Painting::Paintable> m_paintable;
NodeType m_type { NodeType::INVALID };
bool m_needs_layout_tree_update { false };
bool m_child_needs_layout_tree_update { false };

View file

@ -113,7 +113,7 @@ void Range::update_associated_selection()
document.reset_cursor_blink_cycle();
// NB: Called during selection update after range change.
if (auto* viewport = document.unsafe_paintable()) {
if (auto viewport = document.unsafe_paintable()) {
viewport->recompute_selection_states(*this);
viewport->set_needs_repaint();
}
@ -1210,9 +1210,10 @@ GC::Ref<Geometry::DOMRectList> Range::get_client_rects()
// 2. For each Text node selected or partially selected by the range (including when the boundary-points
// are identical), include scaled DOMRect object (for the part that is selected, not the whole line box).
auto const& text = static_cast<DOM::Text const&>(*node);
auto const* paintable = text.paintable();
auto paintable = text.paintable();
if (paintable && selection_state != Painting::Paintable::SelectionState::None) {
if (auto const* paintable_lines = as_if<Painting::PaintableWithLines>(paintable->containing_block())) {
auto containing_block = paintable->containing_block();
if (auto const* paintable_lines = as_if<Painting::PaintableWithLines>(containing_block.ptr())) {
auto fragments = paintable_lines->fragments();
for (auto frag = fragments.begin(); frag != fragments.end(); frag++) {
auto rect = frag->range_rect(selection_state, start_offset(), end_offset());

View file

@ -208,13 +208,13 @@ void dump_tree(StringBuilder& builder, Layout::Node const& layout_node, bool sho
}
auto dump_position = [&] {
if (auto* paintable_box = as_if<Painting::PaintableBox>(layout_node.first_paintable()))
if (auto first_paintable = layout_node.first_paintable(); auto const* paintable_box = as_if<Painting::PaintableBox>(first_paintable.ptr()))
builder.appendff("at {}", paintable_box->absolute_rect().location());
else
builder.appendff("(not painted)");
};
auto dump_box_model = [&] {
if (auto const* paintable_box = as_if<Painting::PaintableBox>(layout_node.first_paintable())) {
if (auto first_paintable = layout_node.first_paintable(); auto const* paintable_box = as_if<Painting::PaintableBox>(first_paintable.ptr())) {
auto const& box_model = paintable_box->box_model();
// Dump the horizontal box properties
builder.appendff(" [{}+{}+{} {} {}+{}+{}]",
@ -385,14 +385,15 @@ void dump_tree(StringBuilder& builder, Layout::Node const& layout_node, bool sho
if (auto const* block_container = as_if<Layout::BlockContainer>(layout_node);
block_container && block_container->children_are_inline() && block_container->paintable_with_lines()) {
size_t fragment_index = 0;
for (auto const& fragment : block_container->paintable_with_lines()->fragments())
auto paintable_with_lines = block_container->paintable_with_lines();
for (auto const& fragment : paintable_with_lines->fragments())
dump_fragment(fragment, fragment_index++);
}
if (is<Layout::InlineNode>(layout_node) && layout_node.first_paintable()) {
auto const& inline_node = static_cast<Layout::InlineNode const&>(layout_node);
for (auto const& paintable : inline_node.paintables()) {
auto const& paintable_with_lines = static_cast<Painting::PaintableWithLines const&>(paintable);
auto const& paintable_with_lines = static_cast<Painting::PaintableWithLines const&>(*paintable);
auto const& fragments = paintable_with_lines.fragments();
for (size_t fragment_index = 0; fragment_index < fragments.size(); ++fragment_index) {
auto const& fragment = fragments[fragment_index];
@ -748,9 +749,9 @@ void dump_tree(StringBuilder& builder, Painting::Paintable const& paintable, boo
else
builder.append(paintable_color_on);
builder.appendff("{}{} ({})", node_paintable.class_name(), color_off, node_paintable.layout_node().debug_description());
builder.appendff("{}{} ({})", node_paintable->class_name(), color_off, node_paintable->layout_node().debug_description());
if (auto const* paintable_box = as_if<Painting::PaintableBox>(node_paintable)) {
if (auto const* paintable_box = as_if<Painting::PaintableBox>(*node_paintable)) {
builder.appendff(" {}", paintable_box->absolute_border_box_rect());
if (paintable_box->has_scrollable_overflow())
@ -761,7 +762,7 @@ void dump_tree(StringBuilder& builder, Painting::Paintable const& paintable, boo
}
builder.append("\n"sv);
for (auto const* child = node_paintable.first_child(); child; child = child->next_sibling())
for (auto child = node_paintable->first_child(); child; child = child->next_sibling())
dump_tree(builder, *child, colorize, indent + 1);
dumped_any = true;
}

View file

@ -1118,7 +1118,7 @@ void FormAssociatedTextControlElement::scroll_cursor_into_view()
if (!text_node)
return;
auto* paintable = text_node->paintable();
auto paintable = text_node->paintable();
if (!paintable)
return;
@ -1140,7 +1140,7 @@ void FormAssociatedTextControlElement::selection_was_changed(SelectionSource sou
if (!text_node)
return;
// NB: Called during selection change handling, layout may be stale.
auto* text_paintable = text_node->unsafe_paintable();
auto text_paintable = text_node->unsafe_paintable();
if (!text_paintable)
return;

View file

@ -676,7 +676,7 @@ int HTMLElement::offset_width() const
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLElementOffsetWidth);
// 1. If the element does not have any associated box return zero and terminate this algorithm.
auto const* box = paintable_box();
auto box = paintable_box();
if (!box)
return 0;
@ -695,7 +695,7 @@ int HTMLElement::offset_height() const
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLElementOffsetHeight);
// 1. If the element does not have any associated box return zero and terminate this algorithm.
auto const* box = paintable_box();
auto box = paintable_box();
if (!box)
return 0;

View file

@ -292,7 +292,7 @@ WebIDL::UnsignedLong HTMLImageElement::width() const
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLImageElementWidth);
// Return the rendered width of the image, in CSS pixels, if the image is being rendered.
if (auto* paintable_box = this->paintable_box())
if (auto paintable_box = this->paintable_box())
return paintable_box->content_width().to_int();
// On setting [the width or height IDL attribute], they must act as if they reflected the respective content attributes of the same name.
@ -323,7 +323,7 @@ WebIDL::UnsignedLong HTMLImageElement::height() const
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLImageElementHeight);
// Return the rendered height of the image, in CSS pixels, if the image is being rendered.
if (auto* paintable_box = this->paintable_box())
if (auto paintable_box = this->paintable_box())
return paintable_box->content_height().to_int();
// On setting [the width or height IDL attribute], they must act as if they reflected the respective content attributes of the same name.
@ -380,7 +380,7 @@ int HTMLImageElement::x() const
// to the element and its ancestors, or zero if there is no box.
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLImageElementX);
auto const* paintable_box = this->paintable_box();
auto paintable_box = this->paintable_box();
if (!paintable_box)
return 0;
@ -399,7 +399,7 @@ int HTMLImageElement::y() const
// to the element and its ancestors, or zero if there is no box.
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLImageElementY);
auto const* paintable_box = this->paintable_box();
auto paintable_box = this->paintable_box();
if (!paintable_box)
return 0;

View file

@ -2376,7 +2376,7 @@ WebIDL::UnsignedLong HTMLInputElement::height() const
return 0;
// Return the rendered height of the image, in CSS pixels, if the image is being rendered.
if (auto* paintable_box = this->paintable_box())
if (auto paintable_box = this->paintable_box())
return paintable_box->content_height().to_int();
// On setting [the width or height IDL attribute], they must act as if they reflected the respective content attributes of the same name.
@ -2411,7 +2411,7 @@ WebIDL::UnsignedLong HTMLInputElement::width() const
return 0;
// Return the rendered width of the image, in CSS pixels, if the image is being rendered.
if (auto* paintable_box = this->paintable_box())
if (auto paintable_box = this->paintable_box())
return paintable_box->content_width().to_int();
// On setting [the width or height IDL attribute], they must act as if they reflected the respective content attributes of the same name.

View file

@ -86,7 +86,7 @@ void HTMLLabelElement::activation_behavior(DOM::Event const& event)
document().update_layout(DOM::UpdateLayoutReason::HTMLLabelElementActivationBehavior);
// Recompute offsetX/offsetY relative to the control element, since the original values are relative to the label.
if (auto const* paintable = control_element->paintable(); paintable && document().navigable()) {
if (auto paintable = control_element->paintable(); paintable && document().navigable()) {
auto scroll_offset = document().navigable()->viewport_scroll_offset();
auto page_position = CSSPixelPoint { CSSPixels(mouse_event.client_x()) + scroll_offset.x(), CSSPixels(mouse_event.client_y()) + scroll_offset.y() };
auto box_position = paintable->box_type_agnostic_position();

View file

@ -2813,7 +2813,7 @@ CSSPixelPoint Navigable::to_top_level_position(CSSPixelPoint a_position)
break;
if (!ancestor->container())
return {};
auto const* paintable = ancestor->container()->paintable();
auto paintable = ancestor->container()->paintable();
if (!paintable)
return {};

View file

@ -489,7 +489,7 @@ String Internals::dump_paintable_tree(GC::Ref<DOM::Node> node)
{
node->document().update_layout(DOM::UpdateLayoutReason::Debugging);
auto* paintable = node->paintable();
auto paintable = node->paintable();
if (!paintable)
return "(no paintable)"_string;

View file

@ -33,7 +33,7 @@ bool AudioBox::can_have_children() const
return dom_node().shadow_root() != nullptr;
}
GC::Ptr<Painting::Paintable> AudioBox::create_paintable() const
RefPtr<Painting::Paintable> AudioBox::create_paintable() const
{
return Painting::PaintableBox::create(*this);
}

View file

@ -22,7 +22,7 @@ public:
virtual bool can_have_children() const override;
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
// Treat the audio element as if it was not a replaced element, sizing based on its content.

View file

@ -23,12 +23,13 @@ BlockContainer::BlockContainer(DOM::Document& document, DOM::Node* node, Nonnull
BlockContainer::~BlockContainer() = default;
Painting::PaintableWithLines const* BlockContainer::paintable_with_lines() const
RefPtr<Painting::PaintableWithLines const> BlockContainer::paintable_with_lines() const
{
return as_if<Painting::PaintableWithLines>(Box::paintable_box());
auto paintable_box = Box::paintable_box();
return as_if<Painting::PaintableWithLines>(paintable_box.ptr());
}
GC::Ptr<Painting::Paintable> BlockContainer::create_paintable() const
RefPtr<Painting::Paintable> BlockContainer::create_paintable() const
{
return Painting::PaintableWithLines::create(*this);
}

View file

@ -21,9 +21,9 @@ public:
BlockContainer(DOM::Document&, DOM::Node*, NonnullOwnPtr<CSS::ComputedValues>);
virtual ~BlockContainer() override;
Painting::PaintableWithLines const* paintable_with_lines() const;
RefPtr<Painting::PaintableWithLines const> paintable_with_lines() const;
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
virtual bool is_block_container() const final { return true; }

View file

@ -48,19 +48,23 @@ void Box::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_contained_abspos_children);
}
GC::Ptr<Painting::Paintable> Box::create_paintable() const
RefPtr<Painting::Paintable> Box::create_paintable() const
{
return Painting::PaintableBox::create(*this);
}
Painting::PaintableBox* Box::paintable_box()
RefPtr<Painting::PaintableBox> Box::paintable_box()
{
return static_cast<Painting::PaintableBox*>(Node::first_paintable());
if (auto paintable = Node::first_paintable())
return static_cast<Painting::PaintableBox&>(*paintable);
return nullptr;
}
Painting::PaintableBox const* Box::paintable_box() const
RefPtr<Painting::PaintableBox const> Box::paintable_box() const
{
return static_cast<Painting::PaintableBox const*>(Node::first_paintable());
if (auto paintable = Node::first_paintable())
return static_cast<Painting::PaintableBox const&>(*paintable);
return nullptr;
}
Optional<CSSPixelFraction> Box::preferred_aspect_ratio() const

View file

@ -31,8 +31,8 @@ class WEB_API Box : public NodeWithStyleAndBoxModelMetrics {
GC_DECLARE_ALLOCATOR(Box);
public:
Painting::PaintableBox const* paintable_box() const;
Painting::PaintableBox* paintable_box();
RefPtr<Painting::PaintableBox const> paintable_box() const;
RefPtr<Painting::PaintableBox> paintable_box();
// https://www.w3.org/TR/css-images-3/#natural-dimensions
virtual CSS::SizeWithAspectRatio natural_size() const { return {}; }
@ -53,7 +53,7 @@ public:
virtual void did_set_content_size() { }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
void add_contained_abspos_child(GC::Ref<Node> child) { m_contained_abspos_children.append(child); }
void clear_contained_abspos_children() { m_contained_abspos_children.clear(); }

View file

@ -27,7 +27,7 @@ CSS::SizeWithAspectRatio CanvasBox::compute_auto_content_box_size() const
return { width, height, CSSPixelFraction(width, height) };
}
GC::Ptr<Painting::Paintable> CanvasBox::create_paintable() const
RefPtr<Painting::Paintable> CanvasBox::create_paintable() const
{
return Painting::CanvasPaintable::create(*this);
}

View file

@ -21,7 +21,7 @@ public:
HTML::HTMLCanvasElement const& dom_node() const { return static_cast<HTML::HTMLCanvasElement const&>(*ReplacedBox::dom_node()); }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
virtual CSS::SizeWithAspectRatio compute_auto_content_box_size() const override;

View file

@ -20,7 +20,7 @@ CheckBox::CheckBox(DOM::Document& document, HTML::HTMLInputElement& element, GC:
CheckBox::~CheckBox() = default;
GC::Ptr<Painting::Paintable> CheckBox::create_paintable() const
RefPtr<Painting::Paintable> CheckBox::create_paintable() const
{
return Painting::CheckBoxPaintable::create(*this);
}

View file

@ -21,7 +21,7 @@ public:
private:
virtual CSS::SizeWithAspectRatio compute_auto_content_box_size() const override { return { 13, 13, {} }; }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
};
}

View file

@ -37,7 +37,7 @@ GC::Ptr<LegendBox const> FieldSetBox::rendered_legend() const
return legend;
}
GC::Ptr<Painting::Paintable> FieldSetBox::create_paintable() const
RefPtr<Painting::Paintable> FieldSetBox::create_paintable() const
{
return Painting::FieldSetPaintable::create(*this);
}

View file

@ -25,7 +25,7 @@ public:
DOM::Element const& dom_node() const { return static_cast<DOM::Element const&>(*BlockContainer::dom_node()); }
GC::Ptr<LegendBox const> rendered_legend() const;
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
virtual bool is_fieldset_box() const final

View file

@ -69,7 +69,7 @@ bool ImageBox::renders_as_alt_text() const
return !m_image_provider.is_image_available();
}
GC::Ptr<Painting::Paintable> ImageBox::create_paintable() const
RefPtr<Painting::Paintable> ImageBox::create_paintable() const
{
return Painting::ImagePaintable::create(*this);
}

View file

@ -21,7 +21,7 @@ public:
bool renders_as_alt_text() const;
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
auto const& image_provider() const { return m_image_provider; }
auto& image_provider() { return m_image_provider; }

View file

@ -23,11 +23,11 @@ InlineNode::InlineNode(DOM::Document& document, DOM::Element* element, GC::Ref<C
InlineNode::~InlineNode() = default;
GC::Ptr<Painting::PaintableWithLines> InlineNode::create_paintable_for_line_with_index(size_t line_index) const
NonnullRefPtr<Painting::PaintableWithLines> InlineNode::create_paintable_for_line_with_index(size_t line_index) const
{
for (auto const& paintable : paintables()) {
if (is<Painting::PaintableWithLines>(paintable)) {
auto const& paintable_with_lines = static_cast<Painting::PaintableWithLines const&>(paintable);
if (is<Painting::PaintableWithLines>(*paintable)) {
auto const& paintable_with_lines = static_cast<Painting::PaintableWithLines const&>(*paintable);
if (paintable_with_lines.line_index() == line_index) {
return const_cast<Painting::PaintableWithLines&>(paintable_with_lines);
}

View file

@ -18,7 +18,7 @@ public:
InlineNode(DOM::Document&, DOM::Element*, GC::Ref<CSS::ComputedProperties>);
virtual ~InlineNode() override;
GC::Ptr<Painting::PaintableWithLines> create_paintable_for_line_with_index(size_t line_index) const;
NonnullRefPtr<Painting::PaintableWithLines> create_paintable_for_line_with_index(size_t line_index) const;
private:
virtual bool is_inline_node() const override { return true; }

View file

@ -9,7 +9,6 @@
#include <AK/Debug.h>
#include <AK/HashMap.h>
#include <AK/Tuple.h>
#include <LibGC/RootHashMap.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/Layout/AvailableSpace.h>
@ -189,7 +188,7 @@ static CSSPixelRect measure_scrollable_overflow(Box const& box, ContainedBoxesMa
auto overflow_directions = physical_overflow_directions(box);
// - All line boxes directly contained by the scroll container.
if (auto const* paintable_with_lines = as_if<Painting::PaintableWithLines>(box.first_paintable())) {
if (auto first_paintable = box.first_paintable(); auto const* paintable_with_lines = as_if<Painting::PaintableWithLines>(first_paintable.ptr())) {
for (auto const& fragment : paintable_with_lines->fragments())
scrollable_overflow_rect.unite(fragment.absolute_rect());
}
@ -315,11 +314,11 @@ void LayoutState::resolve_relative_positions()
auto& node = const_cast<NodeWithStyle&>(used_values.node());
for (auto& paintable : node.paintables()) {
if (!(is<Painting::PaintableWithLines>(paintable) && is<Layout::InlineNode>(paintable.layout_node())))
auto* inline_paintable = as_if<Painting::PaintableWithLines>(paintable.ptr());
if (!inline_paintable || !is<Layout::InlineNode>(inline_paintable->layout_node()))
continue;
auto const& inline_paintable = static_cast<Painting::PaintableWithLines&>(paintable);
for (auto& fragment : inline_paintable.fragments()) {
for (auto& fragment : inline_paintable->fragments()) {
auto const& fragment_node = fragment.layout_node();
if (!is<Layout::NodeWithStyleAndBoxModelMetrics>(*fragment_node.parent()))
continue;
@ -332,7 +331,8 @@ void LayoutState::resolve_relative_positions()
break;
if (ancestor->computed_values().position() == CSS::Positioning::Relative) {
VERIFY(ancestor->first_paintable());
auto const& ancestor_node = as<Painting::PaintableBox>(*ancestor->first_paintable());
auto ancestor_paintable = ancestor->first_paintable();
auto const& ancestor_node = as<Painting::PaintableBox>(*ancestor_paintable);
auto const& inset = ancestor_node.box_model().inset;
offset.translate_by(inset.left, inset.top);
}
@ -343,14 +343,14 @@ void LayoutState::resolve_relative_positions()
});
}
static void build_paint_tree(Node& node, Painting::Paintable* parent_paintable = nullptr)
static void build_paint_tree(Node& node, RefPtr<Painting::Paintable> parent_paintable = nullptr)
{
for (auto& paintable : node.paintables()) {
if (parent_paintable && !paintable.forms_unconnected_subtree()) {
VERIFY(!paintable.parent());
if (parent_paintable && !paintable->forms_unconnected_subtree()) {
VERIFY(!paintable->parent());
parent_paintable->append_child(paintable);
}
paintable.set_dom_node(node.dom_node());
paintable->set_dom_node(node.dom_node());
if (node.dom_node())
node.dom_node()->set_paintable(paintable);
}
@ -361,19 +361,19 @@ static void build_paint_tree(Node& node, Painting::Paintable* parent_paintable =
void LayoutState::commit(Box& root)
{
Painting::Paintable* parent_paintable = nullptr;
RefPtr<Painting::Paintable> parent_paintable;
if (!root.is_viewport()) {
if (auto* existing = as_if<Painting::PaintableBox>(root.first_paintable())) {
parent_paintable = existing->parent();
if (auto existing = root.first_paintable(); auto* existing_box = as_if<Painting::PaintableBox>(existing.ptr())) {
parent_paintable = existing_box->parent();
if (parent_paintable)
parent_paintable->remove_child(*existing);
parent_paintable->remove_child(*existing_box);
}
}
// Cache existing paintables before clearing.
GC::RootHashMap<Node const*, GC::Ref<Painting::PaintableBox>> paintable_cache(root.document().heap());
HashMap<Node const*, NonnullRefPtr<Painting::PaintableBox>> paintable_cache;
root.for_each_in_inclusive_subtree([&](Node& node) {
if (auto* paintable_box = as_if<Painting::PaintableBox>(node.first_paintable())) {
if (auto paintable = node.first_paintable(); auto* paintable_box = as_if<Painting::PaintableBox>(paintable.ptr())) {
// InlineNodes are excluded because they can span multiple lines, with a separate
// InlinePaintable created for each line via create_paintable_for_line_with_index().
// This 1:N relationship between layout node and paintables, combined with the
@ -410,7 +410,7 @@ void LayoutState::commit(Box& root)
});
HashTable<Layout::TextNode*> text_nodes;
HashTable<Painting::PaintableWithLines*> inline_node_paintables;
HashTable<WeakPtr<Painting::PaintableWithLines>> inline_node_paintables;
auto transfer_box_model_metrics = [](Painting::BoxModelMetrics& box_model, UsedValues const& used_values) {
box_model.inset = { used_values.inset_top, used_values.inset_right, used_values.inset_bottom, used_values.inset_left };
@ -430,7 +430,7 @@ void LayoutState::commit(Box& root)
if (auto const* used_values = try_get(inline_node))
transfer_box_model_metrics(line_paintable->box_model(), *used_values);
if (!inline_node_paintables.contains(line_paintable.ptr())) {
inline_node_paintables.set(line_paintable.ptr());
inline_node_paintables.set(line_paintable);
inline_node.add_paintable(line_paintable);
}
return true;
@ -445,7 +445,7 @@ void LayoutState::commit(Box& root)
if (m_subtree_root && !m_subtree_root->is_inclusive_ancestor_of(node))
return;
GC::Ptr<Painting::Paintable> paintable;
RefPtr<Painting::Paintable> paintable;
// Try to reuse cached paintable for Box nodes
if (auto cached = paintable_cache.get(&node); cached.has_value()) {
@ -524,7 +524,8 @@ void LayoutState::commit(Box& root)
if (!node.is_box())
return;
auto& paintable = as<Painting::PaintableBox>(*node.first_paintable());
auto paintable_ref = node.first_paintable();
auto& paintable = as<Painting::PaintableBox>(*paintable_ref);
CSSPixelPoint offset;
if (used_values.containing_line_box_fragment.has_value()) {
@ -559,7 +560,10 @@ void LayoutState::commit(Box& root)
resolve_relative_positions();
// Measure size of paintables created for inline nodes.
for (auto* paintable_with_lines : inline_node_paintables) {
for (auto const& weak_paintable_with_lines : inline_node_paintables) {
auto paintable_with_lines = weak_paintable_with_lines.strong_ref();
if (!paintable_with_lines)
continue;
if (!is<InlineNode>(paintable_with_lines->layout_node()))
continue;
@ -636,7 +640,7 @@ void LayoutState::commit(Box& root)
m_used_values_store.for_each([&](UsedValues& used_values) {
auto& node = used_values.node();
for (auto& paintable : node.paintables()) {
auto* paintable_box = as_if<Painting::PaintableBox>(paintable);
auto* paintable_box = as_if<Painting::PaintableBox>(paintable.ptr());
if (!paintable_box)
continue;
@ -648,7 +652,7 @@ void LayoutState::commit(Box& root)
auto sticky_insets = make<Painting::StickyInsets>();
auto const& inset = node.computed_values().inset();
auto const* nearest_scrollable_ancestor = paintable_box->nearest_scrollable_ancestor();
auto nearest_scrollable_ancestor = paintable_box->nearest_scrollable_ancestor();
CSSPixelSize scrollport_size;
if (nearest_scrollable_ancestor)
scrollport_size = nearest_scrollable_ancestor->absolute_rect().size();

View file

@ -75,7 +75,7 @@ Optional<String> ListItemMarkerBox::text() const
});
}
GC::Ptr<Painting::Paintable> ListItemMarkerBox::create_paintable() const
RefPtr<Painting::Paintable> ListItemMarkerBox::create_paintable() const
{
return Painting::MarkerPaintable::create(*this);
}

View file

@ -24,7 +24,7 @@ public:
Optional<String> text() const;
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
CSS::ListStyleType const& list_style_type() const { return m_list_style_type; }
CSS::ListStylePosition list_style_position() const { return m_list_style_position; }

View file

@ -48,7 +48,7 @@ void NavigableContainerViewport::did_set_content_size()
dom_node().content_navigable()->set_viewport_size(paintable_box()->content_size());
}
GC::Ptr<Painting::Paintable> NavigableContainerViewport::create_paintable() const
RefPtr<Painting::Paintable> NavigableContainerViewport::create_paintable() const
{
return Painting::NavigableContainerViewportPaintable::create(*this);
}

View file

@ -22,7 +22,7 @@ public:
[[nodiscard]] HTML::NavigableContainer const& dom_node() const { return as<HTML::NavigableContainer>(*ReplacedBox::dom_node()); }
[[nodiscard]] HTML::NavigableContainer& dom_node() { return as<HTML::NavigableContainer>(*ReplacedBox::dom_node()); }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
virtual CSS::SizeWithAspectRatio natural_size() const override;

View file

@ -59,9 +59,6 @@ void Node::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_dom_node);
for (auto const& paintable : m_paintable) {
visitor.visit(GC::Ptr { &paintable });
}
visitor.visit(m_containing_block);
visitor.visit(m_inline_containing_block_if_applicable);
visitor.visit(m_pseudo_element_generator);
@ -610,7 +607,7 @@ void NodeWithStyle::ImageObserver::image_style_value_did_update(CSS::ImageStyleV
VERIFY(m_owner);
for (auto& paintable : m_owner->paintables())
paintable.set_needs_repaint();
paintable->set_needs_repaint();
// The body's background propagates to the root element's paintable, which holds the cached draw commands.
if (m_owner->is_body()) {
@ -619,7 +616,7 @@ void NodeWithStyle::ImageObserver::image_style_value_did_update(CSS::ImageStyleV
if (auto html_layout_node = html_element->unsafe_layout_node()) {
if (html_element->should_use_body_background_properties()) {
for (auto& paintable : html_layout_node->paintables())
paintable.set_needs_repaint();
paintable->set_needs_repaint();
}
}
}
@ -1256,7 +1253,7 @@ bool NodeWithStyle::is_scroll_container() const
|| overflow_value_makes_box_a_scroll_container(computed_values().overflow_y());
}
void Node::add_paintable(GC::Ptr<Painting::Paintable> paintable)
void Node::add_paintable(RefPtr<Painting::Paintable> paintable)
{
if (!paintable)
return;
@ -1265,10 +1262,14 @@ void Node::add_paintable(GC::Ptr<Painting::Paintable> paintable)
void Node::clear_paintables()
{
for (auto& paintable : m_paintable) {
if (paintable->parent())
paintable->remove();
}
m_paintable.clear();
}
GC::Ptr<Painting::Paintable> Node::create_paintable() const
RefPtr<Painting::Paintable> Node::create_paintable() const
{
return nullptr;
}

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/DoublyLinkedList.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/NonnullRefPtr.h>
#include <AK/Vector.h>
@ -65,16 +66,26 @@ public:
m_pseudo_element_generator = &element;
}
using PaintableList = IntrusiveList<&Painting::Paintable::m_list_node>;
using PaintableList = DoublyLinkedList<NonnullRefPtr<Painting::Paintable>>;
Painting::Paintable* first_paintable() { return m_paintable.first(); }
Painting::Paintable const* first_paintable() const { return m_paintable.first(); }
RefPtr<Painting::Paintable> first_paintable()
{
if (m_paintable.is_empty())
return nullptr;
return m_paintable.first();
}
RefPtr<Painting::Paintable const> first_paintable() const
{
if (m_paintable.is_empty())
return nullptr;
return m_paintable.first();
}
PaintableList& paintables() { return m_paintable; }
PaintableList const& paintables() const { return m_paintable; }
void add_paintable(GC::Ptr<Painting::Paintable>);
void add_paintable(RefPtr<Painting::Paintable>);
void clear_paintables();
virtual GC::Ptr<Painting::Paintable> create_paintable() const;
virtual RefPtr<Painting::Paintable> create_paintable() const;
DOM::Document& document();
DOM::Document const& document() const;

View file

@ -21,7 +21,7 @@ RadioButton::RadioButton(DOM::Document& document, HTML::HTMLInputElement& elemen
RadioButton::~RadioButton() = default;
GC::Ptr<Painting::Paintable> RadioButton::create_paintable() const
RefPtr<Painting::Paintable> RadioButton::create_paintable() const
{
return Painting::RadioButtonPaintable::create(*this);
}

View file

@ -21,7 +21,7 @@ public:
private:
CSS::SizeWithAspectRatio compute_auto_content_box_size() const override { return { 12, 12, {} }; }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
};
}

View file

@ -17,7 +17,7 @@ SVGClipBox::SVGClipBox(DOM::Document& document, SVG::SVGClipPathElement& element
{
}
GC::Ptr<Painting::Paintable> SVGClipBox::create_paintable() const
RefPtr<Painting::Paintable> SVGClipBox::create_paintable() const
{
return Painting::SVGClipPaintable::create(*this);
}

View file

@ -23,7 +23,7 @@ public:
SVG::SVGClipPathElement& dom_node() { return as<SVG::SVGClipPathElement>(SVGBox::dom_node()); }
SVG::SVGClipPathElement const& dom_node() const { return as<SVG::SVGClipPathElement>(SVGBox::dom_node()); }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
virtual bool is_svg_clip_box() const final { return true; }

View file

@ -17,7 +17,7 @@ SVGForeignObjectBox::SVGForeignObjectBox(DOM::Document& document, SVG::SVGForeig
{
}
GC::Ptr<Painting::Paintable> SVGForeignObjectBox::create_paintable() const
RefPtr<Painting::Paintable> SVGForeignObjectBox::create_paintable() const
{
return Painting::SVGForeignObjectPaintable::create(*this);
}

View file

@ -24,7 +24,7 @@ public:
SVG::SVGForeignObjectElement& dom_node() { return static_cast<SVG::SVGForeignObjectElement&>(*BlockContainer::dom_node()); }
SVG::SVGForeignObjectElement const& dom_node() const { return static_cast<SVG::SVGForeignObjectElement const&>(*BlockContainer::dom_node()); }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
virtual bool is_svg_foreign_object_box() const override { return true; }

View file

@ -19,7 +19,7 @@ SVGGeometryBox::SVGGeometryBox(DOM::Document& document, SVG::SVGGeometryElement&
{
}
GC::Ptr<Painting::Paintable> SVGGeometryBox::create_paintable() const
RefPtr<Painting::Paintable> SVGGeometryBox::create_paintable() const
{
return Painting::SVGPathPaintable::create(*this);
}

View file

@ -23,7 +23,7 @@ public:
SVG::SVGGeometryElement& dom_node() { return static_cast<SVG::SVGGeometryElement&>(SVGGraphicsBox::dom_node()); }
SVG::SVGGeometryElement const& dom_node() const { return static_cast<SVG::SVGGeometryElement const&>(SVGGraphicsBox::dom_node()); }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
virtual bool is_svg_geometry_box() const final { return true; }

View file

@ -17,7 +17,7 @@ SVGGraphicsBox::SVGGraphicsBox(DOM::Document& document, SVG::SVGGraphicsElement&
{
}
GC::Ptr<Painting::Paintable> SVGGraphicsBox::create_paintable() const
RefPtr<Painting::Paintable> SVGGraphicsBox::create_paintable() const
{
return Painting::SVGGraphicsPaintable::create(*this);
}

View file

@ -24,7 +24,7 @@ public:
SVG::SVGGraphicsElement& dom_node() { return as<SVG::SVGGraphicsElement>(SVGBox::dom_node()); }
SVG::SVGGraphicsElement const& dom_node() const { return as<SVG::SVGGraphicsElement>(SVGBox::dom_node()); }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
virtual bool is_svg_graphics_box() const override { return true; }

View file

@ -18,7 +18,7 @@ SVGImageBox::SVGImageBox(DOM::Document& document, SVG::SVGGraphicsElement& eleme
{
}
GC::Ptr<Painting::Paintable> SVGImageBox::create_paintable() const
RefPtr<Painting::Paintable> SVGImageBox::create_paintable() const
{
return Painting::ImagePaintable::create(*this);
}

View file

@ -23,7 +23,7 @@ public:
SVG::SVGImageElement& dom_node() { return static_cast<SVG::SVGImageElement&>(SVGGraphicsBox::dom_node()); }
SVG::SVGImageElement const& dom_node() const { return static_cast<SVG::SVGImageElement const&>(SVGGraphicsBox::dom_node()); }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
};
}

View file

@ -17,7 +17,7 @@ SVGMaskBox::SVGMaskBox(DOM::Document& document, SVG::SVGMaskElement& element, GC
{
}
GC::Ptr<Painting::Paintable> SVGMaskBox::create_paintable() const
RefPtr<Painting::Paintable> SVGMaskBox::create_paintable() const
{
return Painting::SVGMaskPaintable::create(*this);
}

View file

@ -25,7 +25,7 @@ public:
SVG::SVGMaskElement& dom_node() { return as<SVG::SVGMaskElement>(SVGGraphicsBox::dom_node()); }
SVG::SVGMaskElement const& dom_node() const { return as<SVG::SVGMaskElement>(SVGGraphicsBox::dom_node()); }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
};
template<>

View file

@ -16,7 +16,7 @@ SVGPatternBox::SVGPatternBox(DOM::Document& document, SVG::SVGPatternElement& el
{
}
GC::Ptr<Painting::Paintable> SVGPatternBox::create_paintable() const
RefPtr<Painting::Paintable> SVGPatternBox::create_paintable() const
{
return Painting::SVGPatternPaintable::create(*this);
}

View file

@ -22,7 +22,7 @@ public:
SVG::SVGPatternElement& dom_node() { return as<SVG::SVGPatternElement>(SVGBox::dom_node()); }
SVG::SVGPatternElement const& dom_node() const { return as<SVG::SVGPatternElement>(SVGBox::dom_node()); }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
virtual bool is_svg_pattern_box() const final { return true; }

View file

@ -20,7 +20,7 @@ SVGSVGBox::SVGSVGBox(DOM::Document& document, SVG::SVGSVGElement& element, GC::R
{
}
GC::Ptr<Painting::Paintable> SVGSVGBox::create_paintable() const
RefPtr<Painting::Paintable> SVGSVGBox::create_paintable() const
{
return Painting::SVGSVGPaintable::create(*this);
}

View file

@ -24,7 +24,7 @@ public:
virtual bool can_have_children() const override { return true; }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
virtual CSS::SizeWithAspectRatio natural_size() const override;

View file

@ -17,7 +17,7 @@ SVGTextBox::SVGTextBox(DOM::Document& document, SVG::SVGTextPositioningElement&
{
}
GC::Ptr<Painting::Paintable> SVGTextBox::create_paintable() const
RefPtr<Painting::Paintable> SVGTextBox::create_paintable() const
{
return Painting::SVGPathPaintable::create(*this);
}

View file

@ -23,7 +23,7 @@ public:
SVG::SVGTextPositioningElement& dom_node() { return static_cast<SVG::SVGTextPositioningElement&>(SVGGraphicsBox::dom_node()); }
SVG::SVGTextPositioningElement const& dom_node() const { return static_cast<SVG::SVGTextPositioningElement const&>(SVGGraphicsBox::dom_node()); }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
CSSPixelPoint viewbox_origin() const;

View file

@ -16,7 +16,7 @@ SVGTextPathBox::SVGTextPathBox(DOM::Document& document, SVG::SVGTextPathElement&
{
}
GC::Ptr<Painting::Paintable> SVGTextPathBox::create_paintable() const
RefPtr<Painting::Paintable> SVGTextPathBox::create_paintable() const
{
return Painting::SVGPathPaintable::create(*this);
}

View file

@ -22,7 +22,7 @@ public:
SVG::SVGTextPathElement& dom_node() { return static_cast<SVG::SVGTextPathElement&>(SVGGraphicsBox::dom_node()); }
SVG::SVGTextPathElement const& dom_node() const { return static_cast<SVG::SVGTextPathElement const&>(SVGGraphicsBox::dom_node()); }
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
CSSPixelPoint viewbox_origin() const;

View file

@ -804,7 +804,7 @@ Optional<TextNode::Chunk> TextNode::ChunkIterator::try_commit_chunk(size_t start
return {};
}
GC::Ptr<Painting::Paintable> TextNode::create_paintable() const
RefPtr<Painting::Paintable> TextNode::create_paintable() const
{
return Painting::TextPaintable::create(*this);
}

View file

@ -83,7 +83,7 @@ public:
Unicode::Segmenter& grapheme_segmenter() const;
Unicode::Segmenter& line_segmenter() const;
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
virtual bool is_text_node() const final { return true; }

View file

@ -44,7 +44,7 @@ CSS::SizeWithAspectRatio VideoBox::natural_size() const
return { natural_size->width(), natural_size->height(), natural_size->width() / natural_size->height() };
}
GC::Ptr<Painting::Paintable> VideoBox::create_paintable() const
RefPtr<Painting::Paintable> VideoBox::create_paintable() const
{
return Painting::VideoPaintable::create(*this);
}

View file

@ -21,7 +21,7 @@ public:
virtual bool can_have_children() const override;
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
private:
VideoBox(DOM::Document&, DOM::Element&, GC::Ref<CSS::ComputedProperties>);

View file

@ -29,7 +29,7 @@ DOM::Document const& Viewport::dom_node() const
return static_cast<DOM::Document const&>(*Node::dom_node());
}
GC::Ptr<Painting::Paintable> Viewport::create_paintable() const
RefPtr<Painting::Paintable> Viewport::create_paintable() const
{
return Painting::ViewportPaintable::create(*this);
}

View file

@ -35,7 +35,7 @@ public:
virtual void visit_edges(Visitor&) override;
private:
virtual GC::Ptr<Painting::Paintable> create_paintable() const override;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
void update_text_blocks();

View file

@ -120,7 +120,7 @@ CSSPixelPoint AutoScrollHandler::process(CSSPixelPoint mouse_position)
GC::Ptr<DOM::Element> AutoScrollHandler::find_scrollable_ancestor(Painting::Paintable const& paintable)
{
auto* paintable_box = paintable.containing_block();
auto paintable_box = paintable.containing_block();
while (paintable_box) {
if (paintable_box->could_be_scrolled_by_wheel_event()) {
if (auto* element = as_if<DOM::Element>(paintable_box->dom_node().ptr()))
@ -141,7 +141,7 @@ GC::Ptr<DOM::Element> AutoScrollHandler::find_scrollable_ancestor(Painting::Pain
// Returns the paintable box that manages the scrollport for an auto-scroll container element. When the element is the
// document's scrolling element, the viewport paintable is the scroll container.
GC::Ptr<Painting::PaintableBox> AutoScrollHandler::auto_scroll_paintable(DOM::Element& element)
RefPtr<Painting::PaintableBox> AutoScrollHandler::auto_scroll_paintable(DOM::Element& element)
{
if (element.document().scrolling_element().ptr() == &element)
return element.document().paintable();

View file

@ -25,7 +25,7 @@ public:
bool is_active() const { return m_active; }
static GC::Ptr<DOM::Element> find_scrollable_ancestor(Painting::Paintable const&);
static GC::Ptr<Painting::PaintableBox> auto_scroll_paintable(DOM::Element&);
static RefPtr<Painting::PaintableBox> auto_scroll_paintable(DOM::Element&);
private:
void activate();

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGC/WeakInlines.h>
#include <LibWeb/CSS/CSSStyleProperties.h>
#include <LibWeb/CSS/PropertyID.h>
#include <LibWeb/DOM/Element.h>
@ -20,7 +21,7 @@ static Optional<CSSPixelSize> containing_block_padding_box_size(Layout::Node con
auto parent_box = layout_node.containing_block();
if (!parent_box)
return {};
if (auto const* paintable_box = as_if<Painting::PaintableBox>(parent_box->first_paintable()))
if (auto first_paintable = parent_box->first_paintable(); auto const* paintable_box = as_if<Painting::PaintableBox>(first_paintable.ptr()))
return paintable_box->absolute_padding_box_rect().size();
return {};
}
@ -29,14 +30,18 @@ ElementResizeAction::ElementResizeAction(GC::Ref<DOM::Element> element, CSSPixel
: m_element(element)
, m_pointer_down_origin(pointer_down_origin)
{
auto const* paintable_box = m_element->paintable_box();
auto paintable_box = element->paintable_box();
if (paintable_box)
m_initial_border_box_size = paintable_box->absolute_border_box_rect().size();
}
void ElementResizeAction::handle_pointer_move(CSSPixelPoint pointer_position)
{
auto const* paintable_box = m_element->paintable_box();
auto element = m_element.ptr();
if (!element || !element->is_connected())
return;
auto paintable_box = element->paintable_box();
if (!paintable_box)
return;
auto const& layout_node = paintable_box->layout_node();
@ -89,7 +94,7 @@ void ElementResizeAction::handle_pointer_move(CSSPixelPoint pointer_position)
css_height -= metrics.padding.top + metrics.padding.bottom + computed.border_top().width + computed.border_bottom().width;
}
auto style = m_element->style_for_bindings();
auto style = element->style_for_bindings();
auto width_str = MUST(String::formatted("{:.2f}px", max(0.0, css_width.to_double())));
auto height_str = MUST(String::formatted("{:.2f}px", max(0.0, css_height.to_double())));
@ -97,9 +102,4 @@ void ElementResizeAction::handle_pointer_move(CSSPixelPoint pointer_position)
MUST(style->set_property(CSS::PropertyID::Height, height_str));
}
void ElementResizeAction::visit_edges(GC::Cell::Visitor& visitor) const
{
visitor.visit(m_element);
}
}

View file

@ -6,7 +6,7 @@
#pragma once
#include <LibGC/Cell.h>
#include <LibGC/Weak.h>
#include <LibWeb/Forward.h>
#include <LibWeb/PixelUnits.h>
@ -20,10 +20,8 @@ public:
void handle_pointer_move(CSSPixelPoint pointer_position);
void visit_edges(GC::Cell::Visitor&) const;
private:
GC::Ref<DOM::Element> m_element;
GC::Weak<DOM::Element> m_element;
CSSPixelPoint m_pointer_down_origin;
CSSPixelSize m_initial_border_box_size;
};

View file

@ -67,7 +67,7 @@ static GC::Ptr<DOM::Node> dom_node_for_event_dispatch(Painting::Paintable& paint
{
if (auto node = paintable.dom_node())
return node;
auto* parent = paintable.parent();
auto parent = paintable.parent();
while (parent) {
if (auto node = parent->dom_node())
return node;
@ -197,9 +197,9 @@ static CSSPixelPoint compute_mouse_event_offset(CSSPixelPoint position, Painting
// return the x-coordinate of the position where the event occurred,
// ignoring the transforms that apply to the element and its ancestors,
CSSPixelPoint offset_position = position;
if (is<Painting::PaintableBox>(paintable)) {
offset_position = static_cast<Painting::PaintableBox const&>(paintable).inverse_transform_point(position);
} else if (auto* containing_block = paintable.containing_block()) {
if (auto const* paintable_box = as_if<Painting::PaintableBox>(paintable)) {
offset_position = paintable_box->inverse_transform_point(position);
} else if (auto containing_block = paintable.containing_block()) {
offset_position = containing_block->inverse_transform_point(position);
}
@ -550,14 +550,14 @@ EventHandler::EventHandler(Badge<HTML::Navigable>, HTML::Navigable& navigable)
EventHandler::~EventHandler() = default;
GC::Ptr<Painting::PaintableBox> EventHandler::paint_root()
RefPtr<Painting::PaintableBox> EventHandler::paint_root()
{
if (!m_navigable->active_document())
return nullptr;
return m_navigable->active_document()->paintable_box();
}
GC::Ptr<Painting::PaintableBox const> EventHandler::paint_root() const
RefPtr<Painting::PaintableBox const> EventHandler::paint_root() const
{
if (!m_navigable->active_document())
return nullptr;
@ -587,12 +587,12 @@ EventResult EventHandler::handle_mousewheel(CSSPixelPoint visual_viewport_positi
auto handled_event = EventResult::Dropped;
GC::Ptr<Painting::Paintable> paintable;
RefPtr<Painting::Paintable> paintable;
if (auto result = target_for_mouse_position(visual_viewport_position); result.has_value())
paintable = result->paintable;
if (paintable) {
Painting::Paintable* containing_block = paintable;
RefPtr<Painting::Paintable> containing_block = paintable;
while (containing_block) {
auto handled_scroll_event = containing_block->handle_mousewheel({}, visual_viewport_position, buttons, modifiers, wheel_delta_x, wheel_delta_y);
if (handled_scroll_event)
@ -621,7 +621,9 @@ EventResult EventHandler::handle_mousewheel(CSSPixelPoint visual_viewport_positi
return EventResult::Dropped;
auto page_offset = compute_mouse_event_page_offset(viewport_position);
auto const& offset_paintable = layout_node->first_paintable() ? layout_node->first_paintable() : paintable.ptr();
RefPtr<Painting::Paintable> offset_paintable = layout_node->first_paintable();
if (!offset_paintable)
offset_paintable = paintable;
auto scroll_offset = document->navigable()->viewport_scroll_offset();
auto offset = compute_mouse_event_offset(visual_viewport_position.translated(scroll_offset), *offset_paintable);
bool could_scroll_viewport = document->paintable_box()->could_be_scrolled_by_wheel_event();
@ -643,7 +645,7 @@ EventResult EventHandler::handle_mousewheel(CSSPixelPoint visual_viewport_positi
return handled_event;
}
void EventHandler::update_hovered_chrome_widget(GC::Ptr<Painting::ChromeWidget> widget)
void EventHandler::update_hovered_chrome_widget(RefPtr<Painting::ChromeWidget> widget)
{
if (m_hovered_chrome_widget == widget)
return;
@ -657,13 +659,15 @@ void EventHandler::update_hovered_chrome_widget(GC::Ptr<Painting::ChromeWidget>
EventHandler::MouseEventCoordinates EventHandler::compute_mouse_event_coordinates(CSSPixelPoint visual_viewport_position, CSSPixelPoint viewport_position, Painting::Paintable const& paintable, Layout::Node const& layout_node) const
{
auto page_offset = compute_mouse_event_page_offset(viewport_position);
auto const& offset_paintable = layout_node.first_paintable() ? layout_node.first_paintable() : &paintable;
RefPtr<Painting::Paintable const> offset_paintable = layout_node.first_paintable();
if (!offset_paintable)
offset_paintable = paintable;
auto scroll_offset = m_navigable->active_document()->navigable()->viewport_scroll_offset();
auto offset = compute_mouse_event_offset(visual_viewport_position.translated(scroll_offset), *offset_paintable);
return { page_offset, visual_viewport_position, viewport_position, offset };
}
bool EventHandler::dispatch_chrome_widget_pointer_event(GC::Ptr<Painting::ChromeWidget> target, FlyString const& type, unsigned button, CSSPixelPoint visual_viewport_position)
bool EventHandler::dispatch_chrome_widget_pointer_event(RefPtr<Painting::ChromeWidget> target, FlyString const& type, unsigned button, CSSPixelPoint visual_viewport_position)
{
bool allow_default_behavior = true;
@ -688,7 +692,7 @@ bool EventHandler::dispatch_chrome_widget_pointer_event(GC::Ptr<Painting::Chrome
}
// https://w3c.github.io/pointerevents/#mapping-for-devices-that-support-hover
bool EventHandler::dispatch_a_pointer_event_for_a_device_that_supports_hover(PointerEventType type, GC::Ptr<DOM::Node> node, GC::Ptr<Painting::ChromeWidget> chrome_widget, MouseEventCoordinates const& coordinates, CSSPixelPoint screen_position, CSSPixelPoint movement, unsigned button, unsigned buttons, unsigned modifiers, int click_count)
bool EventHandler::dispatch_a_pointer_event_for_a_device_that_supports_hover(PointerEventType type, GC::Ptr<DOM::Node> node, RefPtr<Painting::ChromeWidget> chrome_widget, MouseEventCoordinates const& coordinates, CSSPixelPoint screen_position, CSSPixelPoint movement, unsigned button, unsigned buttons, unsigned modifiers, int click_count)
{
auto& document = *m_navigable->active_document();
auto& realm = document.realm();
@ -837,7 +841,7 @@ static void set_page_cursor(Page& page, Gfx::Cursor cursor)
}
}
void EventHandler::update_cursor(GC::Ptr<Painting::Paintable> paintable, GC::Ptr<DOM::Node> host_element, GC::Ptr<Painting::ChromeWidget> chrome_widget)
void EventHandler::update_cursor(RefPtr<Painting::Paintable> paintable, GC::Ptr<DOM::Node> host_element, RefPtr<Painting::ChromeWidget> chrome_widget)
{
// AD-HOC: Update the cursor image based on the CSS rules before the steps terminate if the target hasn't changed.
auto cursor = [&] -> Gfx::Cursor {
@ -1036,8 +1040,8 @@ EventResult EventHandler::handle_mouseup(CSSPixelPoint visual_viewport_position,
if (!paint_root())
return EventResult::Dropped;
GC::Ptr<Painting::Paintable> paintable;
GC::Ptr<Painting::ChromeWidget> chrome_widget;
RefPtr<Painting::Paintable> paintable;
RefPtr<Painting::ChromeWidget> chrome_widget;
if (auto result = target_for_mouse_position(visual_viewport_position); result.has_value()) {
paintable = result->paintable;
chrome_widget = result->chrome_widget;
@ -1140,11 +1144,11 @@ bool EventHandler::initiate_character_selection(DOM::Document& document, Paintin
bool EventHandler::initiate_word_selection(DOM::Document& document, Painting::HitTestResult const& hit, CSS::UserSelect user_select)
{
if (!is<Painting::TextPaintable>(*hit.paintable))
auto* hit_paintable = as_if<Painting::TextPaintable>(*hit.paintable);
if (!hit_paintable)
return false;
auto& hit_paintable = static_cast<Painting::TextPaintable&>(*hit.paintable);
auto& hit_node = as<DOM::Text>(*hit_paintable.dom_node());
auto& hit_node = as<DOM::Text>(*hit_paintable->dom_node());
size_t previous_boundary = 0;
size_t next_boundary = 0;
@ -1153,7 +1157,7 @@ bool EventHandler::initiate_word_selection(DOM::Document& document, Painting::Hi
next_boundary = hit_node.length_in_utf16_code_units();
} else {
auto& segmenter = word_segmenter();
segmenter.set_segmented_text(hit_paintable.layout_node().text_for_rendering());
segmenter.set_segmented_text(hit_paintable->layout_node().text_for_rendering());
previous_boundary = segmenter.previous_boundary(hit.index_in_node, Unicode::Segmenter::Inclusive::Yes).value_or(0);
next_boundary = segmenter.next_boundary(hit.index_in_node).value_or(hit_node.length());
@ -1310,8 +1314,8 @@ EventResult EventHandler::handle_mousedown(CSSPixelPoint visual_viewport_positio
GC::Ptr<DOM::Node> node;
GC::Ptr<Painting::Paintable> paintable;
GC::Ptr<Painting::ChromeWidget> chrome_widget;
RefPtr<Painting::Paintable> paintable;
RefPtr<Painting::ChromeWidget> chrome_widget;
if (auto result = target_for_mouse_position(visual_viewport_position); result.has_value()) {
paintable = result->paintable;
chrome_widget = result->chrome_widget;
@ -1427,8 +1431,8 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint visual_viewport_positio
}
}
GC::Ptr<Painting::Paintable> paintable;
GC::Ptr<Painting::ChromeWidget> chrome_widget;
RefPtr<Painting::Paintable> paintable;
RefPtr<Painting::ChromeWidget> chrome_widget;
Optional<int> start_index;
if (auto result = target_for_mouse_position(visual_viewport_position); result.has_value()) {
@ -1556,7 +1560,7 @@ EventResult EventHandler::handle_drag_and_drop_event(DragEvent::Type type, CSSPi
if (!paint_root())
return EventResult::Dropped;
GC::Ptr<Painting::Paintable> paintable;
RefPtr<Painting::Paintable> paintable;
if (auto result = target_for_mouse_position(visual_viewport_position); result.has_value())
paintable = result->paintable;
else
@ -1678,7 +1682,7 @@ GC::Ptr<DOM::Node> EventHandler::focus_candidate_for_position(CSSPixelPoint visu
if (!exact_hit.has_value())
return {};
auto focus_dom_node = exact_hit->paintable ? exact_hit->paintable->dom_node() : nullptr;
auto focus_dom_node = exact_hit->paintable->dom_node();
while (focus_dom_node && !focus_dom_node->is_focusable())
focus_dom_node = focus_dom_node->parent_or_shadow_host();
@ -2151,8 +2155,6 @@ bool EventHandler::should_ignore_device_input_event() const
void EventHandler::visit_edges(JS::Cell::Visitor& visitor) const
{
m_drag_and_drop_event_handler->visit_edges(visitor);
visitor.visit(m_hovered_chrome_widget);
visitor.visit(m_captured_chrome_widget);
if (m_mouse_selection_target)
visitor.visit(m_mouse_selection_target->as_cell());
visitor.visit(m_selection_origin);

View file

@ -92,8 +92,8 @@ private:
CSSPixelPoint compute_mouse_event_movement(CSSPixelPoint screen_position) const;
struct Target {
GC::Ptr<Painting::Paintable> paintable;
GC::Ptr<Painting::ChromeWidget> chrome_widget;
RefPtr<Painting::Paintable> paintable;
RefPtr<Painting::ChromeWidget> chrome_widget;
Optional<int> index_in_node;
};
Optional<Target> target_for_mouse_position(CSSPixelPoint position);
@ -112,11 +112,11 @@ private:
PointerMove,
PointerCancel
};
bool dispatch_a_pointer_event_for_a_device_that_supports_hover(PointerEventType, GC::Ptr<DOM::Node>, GC::Ptr<Painting::ChromeWidget>, MouseEventCoordinates const&, CSSPixelPoint screen_position, CSSPixelPoint movement, unsigned button, unsigned buttons, unsigned modifiers, int click_count = 0);
bool dispatch_a_pointer_event_for_a_device_that_supports_hover(PointerEventType, GC::Ptr<DOM::Node>, RefPtr<Painting::ChromeWidget>, MouseEventCoordinates const&, CSSPixelPoint screen_position, CSSPixelPoint movement, unsigned button, unsigned buttons, unsigned modifiers, int click_count = 0);
void track_the_effective_position_of_the_legacy_mouse_pointer(GC::Ptr<DOM::Node>);
void update_cursor(GC::Ptr<Painting::Paintable> paintable, GC::Ptr<DOM::Node> host_element, GC::Ptr<Painting::ChromeWidget> chrome_widget);
bool dispatch_chrome_widget_pointer_event(GC::Ptr<Painting::ChromeWidget>, FlyString const& type, unsigned button, CSSPixelPoint visual_viewport_position);
void update_hovered_chrome_widget(GC::Ptr<Painting::ChromeWidget>);
void update_cursor(RefPtr<Painting::Paintable> paintable, GC::Ptr<DOM::Node> host_element, RefPtr<Painting::ChromeWidget> chrome_widget);
bool dispatch_chrome_widget_pointer_event(RefPtr<Painting::ChromeWidget>, FlyString const& type, unsigned button, CSSPixelPoint visual_viewport_position);
void update_hovered_chrome_widget(RefPtr<Painting::ChromeWidget>);
bool fire_click_events(GC::Ref<DOM::Node>, MouseEventCoordinates const&, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers, int click_count);
void run_activation_behavior(GC::Ref<DOM::Node>, unsigned button, unsigned modifiers);
void maybe_show_context_menu(GC::Ref<DOM::Node>, MouseEventCoordinates const&, CSSPixelPoint screen_position, CSSPixelPoint viewport_position, unsigned buttons, unsigned modifiers);
@ -126,8 +126,8 @@ private:
void update_mouse_selection(CSSPixelPoint visual_viewport_position);
void apply_mouse_selection(CSSPixelPoint visual_viewport_position);
GC::Ptr<Painting::PaintableBox> paint_root();
GC::Ptr<Painting::PaintableBox const> paint_root() const;
RefPtr<Painting::PaintableBox> paint_root();
RefPtr<Painting::PaintableBox const> paint_root() const;
bool should_ignore_device_input_event() const;
@ -141,8 +141,8 @@ private:
InputEventsTarget* m_mouse_selection_target { nullptr };
GC::Ptr<DOM::Range> m_selection_origin;
GC::Ptr<Painting::ChromeWidget> m_hovered_chrome_widget;
GC::Ptr<Painting::ChromeWidget> m_captured_chrome_widget;
RefPtr<Painting::ChromeWidget> m_hovered_chrome_widget;
RefPtr<Painting::ChromeWidget> m_captured_chrome_widget;
NonnullOwnPtr<DragAndDropEventHandler> m_drag_and_drop_event_handler;

View file

@ -25,7 +25,7 @@ MiddleButtonScrollHandler::MiddleButtonScrollHandler(DOM::Element& container, CS
, m_origin(origin)
, m_mouse_position(origin)
{
if (auto* paintable = m_container_element->document().paintable())
if (auto paintable = m_container_element->document().paintable())
paintable->set_needs_repaint();
}
@ -33,7 +33,7 @@ MiddleButtonScrollHandler::~MiddleButtonScrollHandler()
{
if (!m_container_element->document().layout_is_up_to_date())
return;
if (auto* paintable = m_container_element->document().paintable())
if (auto paintable = m_container_element->document().paintable())
paintable->set_needs_repaint();
}

View file

@ -30,7 +30,7 @@ static void append_text_clip_paths(DisplayListRecordingContext& context, Paintab
if (!layout_node.is_in_flow() && !layout_node.is_floating())
return TraversalDecision::SkipChildrenAndContinue;
}
if (auto* paintable_lines = as_if<PaintableWithLines>(sub_paintable)) {
if (auto const* paintable_lines = as_if<PaintableWithLines>(sub_paintable)) {
for (auto const& fragment : paintable_lines->fragments()) {
if (!is<Layout::TextNode>(fragment.layout_node()))
continue;

View file

@ -9,11 +9,9 @@
namespace Web::Painting {
GC_DEFINE_ALLOCATOR(CanvasPaintable);
GC::Ref<CanvasPaintable> CanvasPaintable::create(Layout::CanvasBox const& layout_box)
NonnullRefPtr<CanvasPaintable> CanvasPaintable::create(Layout::CanvasBox const& layout_box)
{
return layout_box.heap().allocate<CanvasPaintable>(layout_box);
return adopt_ref(*new CanvasPaintable(layout_box));
}
CanvasPaintable::CanvasPaintable(Layout::CanvasBox const& layout_box)

View file

@ -12,11 +12,9 @@
namespace Web::Painting {
class CanvasPaintable final : public PaintableBox {
GC_CELL(CanvasPaintable, PaintableBox);
GC_DECLARE_ALLOCATOR(CanvasPaintable);
public:
static GC::Ref<CanvasPaintable> create(Layout::CanvasBox const&);
static NonnullRefPtr<CanvasPaintable> create(Layout::CanvasBox const&);
virtual StringView class_name() const override { return "CanvasPaintable"sv; }
virtual void paint(DisplayListRecordingContext&, PaintPhase) const override;

View file

@ -15,8 +15,6 @@
namespace Web::Painting {
GC_DEFINE_ALLOCATOR(CheckBoxPaintable);
static Gfx::Path check_mark_path(Gfx::IntRect checkbox_rect)
{
Gfx::Path path;
@ -35,10 +33,10 @@ static Gfx::Path check_mark_path(Gfx::IntRect checkbox_rect)
return path.copy_transformed(scale_checkmark_to_fit);
}
GC::Ref<CheckBoxPaintable>
NonnullRefPtr<CheckBoxPaintable>
CheckBoxPaintable::create(Layout::CheckBox const& layout_box)
{
return layout_box.heap().allocate<CheckBoxPaintable>(layout_box);
return adopt_ref(*new CheckBoxPaintable(layout_box));
}
CheckBoxPaintable::CheckBoxPaintable(Layout::CheckBox const& layout_box)

View file

@ -12,11 +12,9 @@
namespace Web::Painting {
class CheckBoxPaintable final : public PaintableBox {
GC_CELL(CheckBoxPaintable, PaintableBox);
GC_DECLARE_ALLOCATOR(CheckBoxPaintable);
public:
static GC::Ref<CheckBoxPaintable> create(Layout::CheckBox const&);
static NonnullRefPtr<CheckBoxPaintable> create(Layout::CheckBox const&);
virtual StringView class_name() const override { return "CheckBoxPaintable"sv; }
virtual void paint(DisplayListRecordingContext&, PaintPhase) const override;

View file

@ -7,6 +7,8 @@
#pragma once
#include <AK/EnumBits.h>
#include <AK/RefCounted.h>
#include <AK/Weakable.h>
#include <LibGC/Cell.h>
#include <LibWeb/CSS/ComputedValues.h>
#include <LibWeb/Forward.h>
@ -22,10 +24,12 @@ enum class MouseAction : u8 {
AK_ENUM_BITWISE_OPERATORS(MouseAction);
class ChromeWidget : public JS::Cell {
GC_CELL(ChromeWidget, JS::Cell);
class ChromeWidget
: public RefCounted<ChromeWidget>
, public Weakable<ChromeWidget> {
public:
virtual ~ChromeWidget() = default;
virtual MouseAction handle_pointer_event(FlyString const& type, unsigned button, CSSPixelPoint visual_viewport_position) = 0;
virtual void mouse_enter() = 0;
virtual void mouse_leave() = 0;

View file

@ -11,11 +11,9 @@
namespace Web::Painting {
GC_DEFINE_ALLOCATOR(FieldSetPaintable);
GC::Ref<FieldSetPaintable> FieldSetPaintable::create(Layout::FieldSetBox const& layout_box)
NonnullRefPtr<FieldSetPaintable> FieldSetPaintable::create(Layout::FieldSetBox const& layout_box)
{
return layout_box.heap().allocate<FieldSetPaintable>(layout_box);
return adopt_ref(*new FieldSetPaintable(layout_box));
}
FieldSetPaintable::FieldSetPaintable(Layout::FieldSetBox const& layout_box)
@ -41,7 +39,7 @@ CSSPixels FieldSetPaintable::effective_border_top() const
// whichever is greater.
auto css_border_top = computed_values().border_top().width;
if (auto legend = layout_box().rendered_legend()) {
auto const* legend_paintable = legend->paintable_box();
auto legend_paintable = legend->paintable_box();
auto legend_margin_box_height = legend_paintable->box_model().margin.top
+ legend_paintable->absolute_border_box_rect().height()
+ legend_paintable->box_model().margin.bottom;
@ -90,7 +88,7 @@ void FieldSetPaintable::paint(DisplayListRecordingContext& context, PaintPhase p
return;
}
auto const* legend_paintable = legend->paintable_box();
auto legend_paintable = legend->paintable_box();
auto legend_border_rect = context.rounded_device_rect(legend_paintable->absolute_border_box_rect());

View file

@ -12,11 +12,9 @@
namespace Web::Painting {
class FieldSetPaintable final : public PaintableBox {
GC_CELL(FieldSetPaintable, PaintableBox);
GC_DECLARE_ALLOCATOR(FieldSetPaintable);
public:
static GC::Ref<FieldSetPaintable> create(Layout::FieldSetBox const&);
static NonnullRefPtr<FieldSetPaintable> create(Layout::FieldSetBox const&);
virtual StringView class_name() const override { return "FieldSetPaintable"sv; }
virtual void paint(DisplayListRecordingContext&, PaintPhase) const override;
virtual void paint_background(DisplayListRecordingContext&) const override;

View file

@ -16,19 +16,17 @@
namespace Web::Painting {
GC_DEFINE_ALLOCATOR(ImagePaintable);
GC::Ref<ImagePaintable> ImagePaintable::create(Layout::SVGImageBox const& layout_box)
NonnullRefPtr<ImagePaintable> ImagePaintable::create(Layout::SVGImageBox const& layout_box)
{
return layout_box.heap().allocate<ImagePaintable>(layout_box, layout_box.dom_node(), false, String {}, true);
return adopt_ref(*new ImagePaintable(layout_box, layout_box.dom_node(), false, String {}, true));
}
GC::Ref<ImagePaintable> ImagePaintable::create(Layout::ImageBox const& layout_box)
NonnullRefPtr<ImagePaintable> ImagePaintable::create(Layout::ImageBox const& layout_box)
{
String alt;
if (auto element = layout_box.dom_node())
alt = element->get_attribute_value(HTML::AttributeNames::alt);
return layout_box.heap().allocate<ImagePaintable>(layout_box, layout_box.image_provider(), layout_box.renders_as_alt_text(), move(alt), false);
return adopt_ref(*new ImagePaintable(layout_box, layout_box.image_provider(), layout_box.renders_as_alt_text(), move(alt), false));
}
ImagePaintable::ImagePaintable(Layout::Box const& layout_box, Layout::ImageProvider const& image_provider, bool renders_as_alt_text, String alt_text, bool is_svg_image)
@ -40,12 +38,6 @@ ImagePaintable::ImagePaintable(Layout::Box const& layout_box, Layout::ImageProvi
{
}
void ImagePaintable::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
m_image_provider.image_provider_visit_edges(visitor);
}
void ImagePaintable::reset_for_relayout()
{
PaintableBox::reset_for_relayout();

View file

@ -13,20 +13,15 @@
namespace Web::Painting {
class ImagePaintable final : public PaintableBox {
GC_CELL(ImagePaintable, PaintableBox);
GC_DECLARE_ALLOCATOR(ImagePaintable);
public:
static GC::Ref<ImagePaintable> create(Layout::ImageBox const& layout_box);
static GC::Ref<ImagePaintable> create(Layout::SVGImageBox const& layout_box);
static NonnullRefPtr<ImagePaintable> create(Layout::ImageBox const& layout_box);
static NonnullRefPtr<ImagePaintable> create(Layout::SVGImageBox const& layout_box);
virtual StringView class_name() const override { return "ImagePaintable"sv; }
virtual void paint(DisplayListRecordingContext&, PaintPhase) const override;
virtual void reset_for_relayout() override;
private:
// ^JS::Cell
virtual void visit_edges(Visitor&) override;
ImagePaintable(Layout::Box const& layout_box, Layout::ImageProvider const& image_provider, bool renders_as_alt_text, String alt_text, bool is_svg_image);
bool m_renders_as_alt_text { false };

View file

@ -12,11 +12,9 @@
namespace Web::Painting {
GC_DEFINE_ALLOCATOR(MarkerPaintable);
GC::Ref<MarkerPaintable> MarkerPaintable::create(Layout::ListItemMarkerBox const& layout_box)
NonnullRefPtr<MarkerPaintable> MarkerPaintable::create(Layout::ListItemMarkerBox const& layout_box)
{
return layout_box.heap().allocate<MarkerPaintable>(layout_box);
return adopt_ref(*new MarkerPaintable(layout_box));
}
MarkerPaintable::MarkerPaintable(Layout::ListItemMarkerBox const& layout_box)

View file

@ -12,11 +12,9 @@
namespace Web::Painting {
class MarkerPaintable final : public PaintableBox {
GC_CELL(MarkerPaintable, PaintableBox);
GC_DECLARE_ALLOCATOR(MarkerPaintable);
public:
static GC::Ref<MarkerPaintable> create(Layout::ListItemMarkerBox const&);
static NonnullRefPtr<MarkerPaintable> create(Layout::ListItemMarkerBox const&);
virtual StringView class_name() const override { return "MarkerPaintable"sv; }
virtual void paint(DisplayListRecordingContext&, PaintPhase) const override;

View file

@ -16,11 +16,9 @@
namespace Web::Painting {
GC_DEFINE_ALLOCATOR(NavigableContainerViewportPaintable);
GC::Ref<NavigableContainerViewportPaintable> NavigableContainerViewportPaintable::create(Layout::NavigableContainerViewport const& layout_box)
NonnullRefPtr<NavigableContainerViewportPaintable> NavigableContainerViewportPaintable::create(Layout::NavigableContainerViewport const& layout_box)
{
return layout_box.heap().allocate<NavigableContainerViewportPaintable>(layout_box);
return adopt_ref(*new NavigableContainerViewportPaintable(layout_box));
}
NavigableContainerViewportPaintable::NavigableContainerViewportPaintable(Layout::NavigableContainerViewport const& layout_box)

View file

@ -12,13 +12,11 @@
namespace Web::Painting {
class NavigableContainerViewportPaintable final : public PaintableBox {
GC_CELL(NavigableContainerViewportPaintable, PaintableBox);
GC_DECLARE_ALLOCATOR(NavigableContainerViewportPaintable);
public:
virtual bool is_navigable_container_viewport_paintable() const override { return true; }
static GC::Ref<NavigableContainerViewportPaintable> create(Layout::NavigableContainerViewport const&);
static NonnullRefPtr<NavigableContainerViewportPaintable> create(Layout::NavigableContainerViewport const&);
virtual StringView class_name() const override { return "NavigableContainerViewportPaintable"sv; }
virtual void paint(DisplayListRecordingContext&, PaintPhase) const override;

View file

@ -46,22 +46,6 @@ Paintable::Paintable(Layout::Node const& layout_node)
Paintable::~Paintable() = default;
void Paintable::finalize()
{
Base::finalize();
if (m_list_node.is_in_list())
m_list_node.remove();
}
void Paintable::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
TreeNode::visit_edges(visitor);
visitor.visit(m_dom_node);
visitor.visit(m_layout_node);
visitor.visit(m_containing_block);
}
String Paintable::debug_description() const
{
return MUST(String::formatted("{}({})", class_name(), layout_node().debug_description()));
@ -77,19 +61,29 @@ DOM::Document& Paintable::document()
return layout_node().document();
}
PaintableBox* Paintable::containing_block() const
RefPtr<PaintableBox> Paintable::containing_block() const
{
return m_containing_block.ensure([&] -> GC::Ptr<PaintableBox> {
auto containing_layout_box = m_layout_node->containing_block();
if (m_containing_block.has_value()) {
if (auto containing_block = m_containing_block->strong_ref())
return containing_block;
}
auto containing_block = [&] -> RefPtr<PaintableBox> {
auto containing_layout_box = layout_node().containing_block();
if (!containing_layout_box)
return nullptr;
return const_cast<PaintableBox*>(containing_layout_box->paintable_box());
});
auto paintable_box = containing_layout_box->paintable_box();
if (!paintable_box)
return nullptr;
return const_cast<PaintableBox&>(*paintable_box);
}();
m_containing_block = containing_block;
return containing_block;
}
CSS::ImmutableComputedValues const& Paintable::computed_values() const
{
return m_layout_node->computed_values();
return layout_node().computed_values();
}
bool Paintable::visible_for_hit_testing() const
@ -101,17 +95,17 @@ bool Paintable::visible_for_hit_testing() const
void Paintable::set_dom_node(GC::Ptr<DOM::Node> dom_node)
{
m_dom_node = dom_node;
m_dom_node = dom_node.ptr();
}
GC::Ptr<DOM::Node> Paintable::dom_node()
{
return m_dom_node;
return m_dom_node.ptr();
}
GC::Ptr<DOM::Node const> Paintable::dom_node() const
{
return m_dom_node;
return m_dom_node.ptr();
}
GC::Ptr<HTML::Navigable> Paintable::navigable() const
@ -131,18 +125,19 @@ TraversalDecision Paintable::hit_test(CSSPixelPoint, HitTestType, Function<Trave
bool Paintable::has_stacking_context() const
{
if (is_paintable_box())
return static_cast<PaintableBox const&>(*this).stacking_context();
if (auto const* paintable_box = as_if<PaintableBox>(this))
return paintable_box->stacking_context();
return false;
}
StackingContext* Paintable::enclosing_stacking_context()
RefPtr<StackingContext> Paintable::enclosing_stacking_context()
{
for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
if (!ancestor->is_paintable_box())
for (auto ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
auto* paintable_box = as_if<PaintableBox>(ancestor.ptr());
if (!paintable_box)
continue;
if (auto* stacking_context = static_cast<PaintableBox&>(*ancestor).stacking_context())
return const_cast<StackingContext*>(stacking_context);
if (auto stacking_context = paintable_box->stacking_context())
return stacking_context;
}
// We should always reach the viewport's stacking context.
VERIFY_NOT_REACHED();
@ -151,12 +146,16 @@ StackingContext* Paintable::enclosing_stacking_context()
void Paintable::paint_inspector_overlay(DisplayListRecordingContext& context) const
{
auto& display_list_recorder = context.display_list_recorder();
auto const* paintable_box = as_if<PaintableBox>(this);
if (!paintable_box)
RefPtr<PaintableBox const> paintable_box;
if (is<PaintableBox>(*this))
paintable_box = static_cast<PaintableBox const&>(*this);
else
paintable_box = first_ancestor_of_type<PaintableBox>();
if (paintable_box) {
auto& visual_context_tree = const_cast<ViewportPaintable*>(document().paintable())->visual_context_tree();
auto viewport_paintable = document().paintable();
VERIFY(viewport_paintable);
auto& visual_context_tree = const_cast<ViewportPaintable&>(*viewport_paintable).visual_context_tree();
auto visual_context_index = paintable_box->accumulated_visual_context_index();
if (visual_context_index.value()) {
@ -189,7 +188,7 @@ void Paintable::paint_inspector_overlay(DisplayListRecordingContext& context) co
void Paintable::set_needs_repaint(InvalidateDisplayList should_invalidate_display_list)
{
if (should_invalidate_display_list == InvalidateDisplayList::Yes) {
if (auto* containing_block = this->containing_block())
if (auto containing_block = this->containing_block())
containing_block->invalidate_paint_cache();
}
document().set_needs_repaint(Badge<Painting::Paintable> {}, should_invalidate_display_list);
@ -197,13 +196,13 @@ void Paintable::set_needs_repaint(InvalidateDisplayList should_invalidate_displa
CSSPixelPoint Paintable::box_type_agnostic_position() const
{
if (is_paintable_box())
return static_cast<PaintableBox const*>(this)->absolute_position();
if (auto const* paintable_box = as_if<PaintableBox>(this))
return paintable_box->absolute_position();
VERIFY(is_inline());
CSSPixelPoint position;
if (auto const* block = containing_block(); block && is<Painting::PaintableWithLines>(*block)) {
if (auto block = containing_block(); block && is<Painting::PaintableWithLines>(*block)) {
auto const& fragments = static_cast<Painting::PaintableWithLines const&>(*block).fragments();
if (!fragments.is_empty()) {
position = fragments[0].absolute_rect().location();
@ -352,11 +351,11 @@ void Paintable::set_selection_state(SelectionState state)
m_selection_state = state;
if (auto* box = as_if<PaintableBox>(this)) {
box->invalidate_paint_cache();
} else if (auto* containing_block = this->containing_block()) {
} else if (auto containing_block = this->containing_block()) {
containing_block->invalidate_paint_cache();
for (auto const* ancestor = layout_node().parent(); ancestor && ancestor != &containing_block->layout_node(); ancestor = ancestor->parent()) {
for (auto& paintable : ancestor->paintables()) {
if (auto* ancestor_box = as_if<PaintableBox>(paintable))
if (auto* ancestor_box = as_if<PaintableBox>(paintable.ptr()))
ancestor_box->invalidate_paint_cache();
}
}
@ -366,11 +365,12 @@ void Paintable::set_selection_state(SelectionState state)
void Paintable::scroll_ancestor_to_offset_into_view(size_t offset)
{
// Walk up to find the containing PaintableWithLines.
GC::Ptr<PaintableWithLines const> paintable_with_lines;
for (auto* ancestor = this; ancestor; ancestor = ancestor->parent()) {
paintable_with_lines = as_if<PaintableWithLines>(*ancestor);
if (paintable_with_lines)
RefPtr<PaintableWithLines const> paintable_with_lines;
for (RefPtr<Paintable> ancestor = *this; ancestor; ancestor = ancestor->parent()) {
if (auto* ancestor_lines = as_if<PaintableWithLines>(*ancestor)) {
paintable_with_lines = *ancestor_lines;
break;
}
}
if (!paintable_with_lines)
return;
@ -385,7 +385,7 @@ void Paintable::scroll_ancestor_to_offset_into_view(size_t offset)
auto cursor_rect = fragment.range_rect(SelectionState::StartAndEnd, offset, offset);
// Walk up the containing block chain to find the nearest scrollable ancestor.
for (auto* ancestor = containing_block(); ancestor; ancestor = ancestor->containing_block()) {
for (auto ancestor = containing_block(); ancestor; ancestor = ancestor->containing_block()) {
if (ancestor->has_scrollable_overflow()) {
ancestor->scroll_into_view(cursor_rect);
break;

View file

@ -6,8 +6,11 @@
#pragma once
#include <AK/RefCounted.h>
#include <AK/WeakPtr.h>
#include <AK/Weakable.h>
#include <LibGC/Ptr.h>
#include <LibGC/Root.h>
#include <LibGC/Weak.h>
#include <LibWeb/CSS/ComputedValues.h>
#include <LibWeb/CSS/Display.h>
#include <LibWeb/Export.h>
@ -15,8 +18,8 @@
#include <LibWeb/InvalidateDisplayList.h>
#include <LibWeb/Painting/ShadowData.h>
#include <LibWeb/PixelUnits.h>
#include <LibWeb/RefCountedTreeNode.h>
#include <LibWeb/TraversalDecision.h>
#include <LibWeb/TreeNode.h>
namespace Web::Painting {
@ -32,8 +35,8 @@ enum class PaintPhase {
};
struct HitTestResult {
GC::Root<Paintable> paintable;
GC::Ptr<ChromeWidget> chrome_widget {};
NonnullRefPtr<Paintable> paintable;
RefPtr<ChromeWidget> chrome_widget {};
size_t index_in_node { 0 };
Optional<CSSPixels> vertical_distance {};
Optional<CSSPixels> horizontal_distance {};
@ -55,15 +58,15 @@ enum class HitTestType {
};
class WEB_API Paintable
: public JS::Cell
, public TreeNode<Paintable> {
GC_CELL(Paintable, JS::Cell);
: public RefCounted<Paintable>
, public Weakable<Paintable>
, public RefCountedTreeNode<Paintable> {
public:
static constexpr bool OVERRIDES_FINALIZE = true;
virtual ~Paintable();
virtual StringView class_name() const { return "Paintable"sv; }
void detach_from_layout_node();
[[nodiscard]] bool is_visible() const
@ -80,7 +83,7 @@ public:
[[nodiscard]] CSS::Display display() const { return m_display; }
bool has_stacking_context() const;
StackingContext* enclosing_stacking_context();
RefPtr<StackingContext> enclosing_stacking_context();
virtual void paint(DisplayListRecordingContext&, PaintPhase) const { }
void paint_inspector_overlay(DisplayListRecordingContext&) const;
@ -91,8 +94,12 @@ public:
virtual bool handle_mousewheel(Badge<EventHandler>, CSSPixelPoint, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y);
Layout::Node const& layout_node() const { return m_layout_node; }
Layout::Node& layout_node() { return const_cast<Layout::Node&>(*m_layout_node); }
Layout::Node const& layout_node() const
{
VERIFY(m_layout_node);
return *m_layout_node;
}
Layout::Node& layout_node() { return const_cast<Layout::Node&>(const_cast<Paintable const&>(*this).layout_node()); }
[[nodiscard]] GC::Ptr<DOM::Node> dom_node();
[[nodiscard]] GC::Ptr<DOM::Node const> dom_node() const;
@ -106,7 +113,7 @@ public:
virtual void set_needs_repaint(InvalidateDisplayList = InvalidateDisplayList::Yes);
PaintableBox* containing_block() const;
RefPtr<PaintableBox> containing_block() const;
template<typename T>
bool fast_is() const = delete;
@ -160,22 +167,17 @@ public:
[[nodiscard]] String debug_description() const;
virtual void finalize() override;
friend class Layout::Node;
protected:
explicit Paintable(Layout::Node const&);
virtual void paint_inspector_overlay_internal(DisplayListRecordingContext&) const { }
virtual void visit_edges(Cell::Visitor&) override;
Optional<GC::Ptr<PaintableBox>> mutable m_containing_block;
Optional<WeakPtr<PaintableBox>> mutable m_containing_block;
private:
IntrusiveListNode<Paintable> m_list_node;
GC::Ptr<DOM::Node> m_dom_node;
GC::Ref<Layout::Node const> m_layout_node;
GC::Weak<DOM::Node> m_dom_node;
GC::Weak<Layout::Node const> m_layout_node;
SelectionState m_selection_state { SelectionState::None };

View file

@ -34,8 +34,6 @@
namespace Web::Painting {
GC_DEFINE_ALLOCATOR(PaintableBox);
static bool g_paint_viewport_scrollbars = true;
void set_paint_viewport_scrollbars(bool const enabled)
@ -98,7 +96,7 @@ ResolvedCSSFilter resolve_css_filter(CSS::Filter const& computed_filter, Paintab
result.svg_filter = filter_element->gfx_filter(layout_node);
auto bounds = paintable_box.absolute_border_box_rect();
if (bounds.is_empty()) {
if (auto const* svg_ancestor = paintable_box.first_ancestor_of_type<SVGSVGPaintable>())
if (auto svg_ancestor = paintable_box.first_ancestor_of_type<SVGSVGPaintable>())
result.svg_filter_bounds = svg_ancestor->absolute_rect();
}
if (!bounds.is_empty())
@ -109,14 +107,14 @@ ResolvedCSSFilter resolve_css_filter(CSS::Filter const& computed_filter, Paintab
return result;
}
GC::Ref<PaintableBox> PaintableBox::create(Layout::Box const& layout_box)
NonnullRefPtr<PaintableBox> PaintableBox::create(Layout::Box const& layout_box)
{
return layout_box.heap().allocate<PaintableBox>(layout_box);
return adopt_ref(*new PaintableBox(layout_box));
}
GC::Ref<PaintableBox> PaintableBox::create(Layout::InlineNode const& layout_box)
NonnullRefPtr<PaintableBox> PaintableBox::create(Layout::InlineNode const& layout_box)
{
return layout_box.heap().allocate<PaintableBox>(layout_box);
return adopt_ref(*new PaintableBox(layout_box));
}
PaintableBox::PaintableBox(Layout::Box const& layout_box)
@ -169,15 +167,6 @@ void PaintableBox::reset_for_relayout()
invalidate_stacking_context();
}
void PaintableBox::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_stacking_context);
visitor.visit(m_horizontal_scrollbar);
visitor.visit(m_vertical_scrollbar);
visitor.visit(m_resize_handle);
}
CSSPixelPoint PaintableBox::scroll_offset() const
{
if (is_viewport_paintable()) {
@ -308,7 +297,7 @@ CSSPixelPoint PaintableBox::offset() const
CSSPixelRect PaintableBox::compute_absolute_rect() const
{
CSSPixelRect rect { offset(), content_size() };
for (auto const* block = containing_block(); block; block = block->containing_block())
for (auto block = containing_block(); block; block = block->containing_block())
rect.translate_by(block->offset());
return rect;
}
@ -402,7 +391,7 @@ static CSSPixelRect united_rect_for_continuation_chain(PaintableBox const& start
for (auto const& paintable : node->paintables()) {
if (!is<PaintableBox>(paintable))
continue;
auto const& paintable_box = static_cast<PaintableBox const&>(paintable);
auto const& paintable_box = static_cast<PaintableBox const&>(*paintable);
auto paintable_border_box_rect = get_rect(paintable_box);
if (!result.has_value())
result = paintable_border_box_rect;
@ -444,16 +433,16 @@ Optional<CSSPixelRect> PaintableBox::get_clip_rect() const
return {};
}
GC::Ptr<Scrollbar> PaintableBox::scrollbar(ScrollDirection direction) const
RefPtr<Scrollbar> PaintableBox::scrollbar(ScrollDirection direction) const
{
return direction == ScrollDirection::Horizontal ? m_horizontal_scrollbar : m_vertical_scrollbar;
}
GC::Ref<Scrollbar> PaintableBox::ensure_scrollbar(ScrollDirection direction)
NonnullRefPtr<Scrollbar> PaintableBox::ensure_scrollbar(ScrollDirection direction)
{
auto& slot = direction == ScrollDirection::Horizontal ? m_horizontal_scrollbar : m_vertical_scrollbar;
if (!slot)
slot = Scrollbar::create(heap(), const_cast<PaintableBox&>(*this), direction);
slot = Scrollbar::create(const_cast<PaintableBox&>(*this), direction);
return *slot;
}
@ -800,11 +789,21 @@ void PaintableBox::paint_inspector_overlay_internal(DisplayListRecordingContext&
context.display_list_recorder().draw_text(size_text_device_rect, size_text, font->with_size(font->point_size() * context.device_pixels_per_css_pixel()), Gfx::TextAlignment::Center, context.palette().color(Gfx::ColorRole::TooltipText));
}
void PaintableBox::set_stacking_context(GC::Ref<StackingContext> stacking_context)
void PaintableBox::set_stacking_context(NonnullRefPtr<StackingContext> stacking_context)
{
m_stacking_context = move(stacking_context);
}
RefPtr<StackingContext> PaintableBox::stacking_context()
{
return m_stacking_context;
}
RefPtr<StackingContext const> PaintableBox::stacking_context() const
{
return m_stacking_context;
}
void PaintableBox::invalidate_stacking_context()
{
m_stacking_context = nullptr;
@ -1013,15 +1012,15 @@ bool PaintableBox::is_chrome_mirrored() const
|| writing_mode == CSS::WritingMode::SidewaysRl;
}
GC::Ptr<ResizeHandle> PaintableBox::resize_handle() const
RefPtr<ResizeHandle> PaintableBox::resize_handle() const
{
return m_resize_handle;
}
GC::Ref<ResizeHandle> PaintableBox::ensure_resize_handle()
NonnullRefPtr<ResizeHandle> PaintableBox::ensure_resize_handle()
{
if (!m_resize_handle)
m_resize_handle = ResizeHandle::create(heap(), *this);
m_resize_handle = ResizeHandle::create(*this);
return *m_resize_handle;
}
@ -1168,7 +1167,7 @@ Optional<HitTestResult> PaintableBox::hit_test(CSSPixelPoint position, HitTestTy
TraversalDecision PaintableBox::hit_test_children(CSSPixelPoint position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const
{
for (auto const* child = last_child(); child; child = child->previous_sibling()) {
for (auto child = last_child(); child; child = child->previous_sibling()) {
if (child->is_positioned() && child->computed_values().z_index().value_or(0) == 0)
continue;
if (child->has_stacking_context())
@ -1252,7 +1251,7 @@ CSSPixelRect PaintableBox::transform_reference_box() const
// FIXME: If a viewBox attribute is specified for the SVG viewport creating element:
// - The reference box is positioned at the origin of the coordinate system established by the viewBox attribute.
// - The dimension of the reference box is set to the width and height values of the viewBox attribute.
auto* svg_paintable = first_ancestor_of_type<Painting::SVGSVGPaintable>();
auto svg_paintable = first_ancestor_of_type<Painting::SVGSVGPaintable>();
if (!svg_paintable)
return absolute_border_box_rect();
return svg_paintable->absolute_rect();
@ -1286,7 +1285,7 @@ ScrollFrameIndex PaintableBox::nearest_scroll_frame_index() const
{
if (is_fixed_position())
return {};
auto const* paintable = this->containing_block();
auto paintable = this->containing_block();
while (paintable) {
if (paintable->own_scroll_frame_index().value())
return paintable->own_scroll_frame_index();
@ -1299,9 +1298,9 @@ ScrollFrameIndex PaintableBox::nearest_scroll_frame_index() const
return {};
}
PaintableBox const* PaintableBox::nearest_scrollable_ancestor() const
RefPtr<PaintableBox const> PaintableBox::nearest_scrollable_ancestor() const
{
auto const* paintable = this->containing_block();
auto paintable = this->containing_block();
while (paintable) {
if (paintable->could_be_scrolled_by_wheel_event())
return paintable;

View file

@ -8,6 +8,8 @@
#pragma once
#include <AK/Array.h>
#include <AK/NonnullRefPtr.h>
#include <AK/RefPtr.h>
#include <LibGfx/Forward.h>
#include <LibWeb/CSS/StyleValues/GridTrackSizeListStyleValue.h>
#include <LibWeb/Forward.h>
@ -32,21 +34,19 @@ WEB_API void set_paint_viewport_scrollbars(bool enabled);
ResolvedCSSFilter resolve_css_filter(CSS::Filter const& computed_filter, PaintableBox const& paintable_box);
class WEB_API PaintableBox : public Paintable {
GC_CELL(PaintableBox, Paintable);
GC_DECLARE_ALLOCATOR(PaintableBox);
public:
static GC::Ref<PaintableBox> create(Layout::Box const&);
static GC::Ref<PaintableBox> create(Layout::InlineNode const&);
static NonnullRefPtr<PaintableBox> create(Layout::Box const&);
static NonnullRefPtr<PaintableBox> create(Layout::InlineNode const&);
virtual ~PaintableBox();
virtual StringView class_name() const override { return "PaintableBox"sv; }
virtual void reset_for_relayout();
virtual void paint(DisplayListRecordingContext&, PaintPhase) const override;
StackingContext* stacking_context() { return m_stacking_context; }
StackingContext const* stacking_context() const { return m_stacking_context; }
void set_stacking_context(GC::Ref<StackingContext>);
RefPtr<StackingContext> stacking_context();
RefPtr<StackingContext const> stacking_context() const;
void set_stacking_context(NonnullRefPtr<StackingContext>);
void invalidate_stacking_context();
Optional<int> effective_z_index() const;
@ -174,8 +174,8 @@ public:
ScrollStateSnapshot const* = nullptr) const;
Optional<CSSPixelRect> absolute_scrollbar_rect(ScrollDirection direction, bool with_gutter, ChromeMetrics const& chrome_metrics) const;
GC::Ptr<Scrollbar> scrollbar(ScrollDirection) const;
GC::Ref<Scrollbar> ensure_scrollbar(ScrollDirection);
RefPtr<Scrollbar> scrollbar(ScrollDirection) const;
NonnullRefPtr<Scrollbar> ensure_scrollbar(ScrollDirection);
enum class ConflictingElementKind {
Cell,
@ -240,14 +240,14 @@ public:
bool is_chrome_mirrored() const;
bool has_resizer() const;
GC::Ptr<ResizeHandle> resize_handle() const;
GC::Ref<ResizeHandle> ensure_resize_handle();
RefPtr<ResizeHandle> resize_handle() const;
NonnullRefPtr<ResizeHandle> ensure_resize_handle();
CSSPixelRect transform_reference_box() const;
ScrollFrameIndex nearest_scroll_frame_index() const;
PaintableBox const* nearest_scrollable_ancestor() const;
RefPtr<PaintableBox const> nearest_scrollable_ancestor() const;
using StickyInsets = Painting::StickyInsets;
StickyInsets const& sticky_insets() const { return *m_sticky_insets; }
@ -301,8 +301,6 @@ protected:
explicit PaintableBox(Layout::Box const&);
explicit PaintableBox(Layout::InlineNode const&);
virtual void visit_edges(Visitor&) override;
virtual void paint_border(DisplayListRecordingContext&) const;
virtual void paint_backdrop_filter(DisplayListRecordingContext&) const;
virtual void paint_background(DisplayListRecordingContext&) const;
@ -325,7 +323,7 @@ private:
void paint_middle_button_scroll_indicator(DisplayListRecordingContext&) const;
GC::Ptr<StackingContext> m_stacking_context;
RefPtr<StackingContext> m_stacking_context;
Optional<OverflowData> m_overflow_data;
@ -346,9 +344,9 @@ private:
ResolvedCSSFilter m_filter;
GC::Ptr<Scrollbar> m_horizontal_scrollbar;
GC::Ptr<Scrollbar> m_vertical_scrollbar;
GC::Ptr<ResizeHandle> m_resize_handle;
RefPtr<Scrollbar> m_horizontal_scrollbar;
RefPtr<Scrollbar> m_vertical_scrollbar;
RefPtr<ResizeHandle> m_resize_handle;
bool m_has_non_invertible_css_transform { false };
OwnPtr<StickyInsets> m_sticky_insets;

View file

@ -52,7 +52,7 @@ PaintableFragment::PaintableFragment(Layout::LineBoxFragment const& fragment)
CSSPixelRect const PaintableFragment::absolute_rect() const
{
CSSPixelRect rect { offset(), size() };
if (auto const* containing_block = paintable().containing_block())
if (auto containing_block = paintable().containing_block())
rect.translate_by(containing_block->absolute_position());
return rect;
}
@ -294,9 +294,10 @@ CSSPixelRect PaintableFragment::selection_rect() const
Utf16View PaintableFragment::text() const
{
if (!is<TextPaintable>(paintable()))
auto const* text_paintable = as_if<TextPaintable>(paintable());
if (!text_paintable)
return {};
return as<TextPaintable>(paintable()).layout_node().text_for_rendering().substring_view(m_start_offset, m_length_in_code_units);
return text_paintable->layout_node().text_for_rendering().substring_view(m_start_offset, m_length_in_code_units);
}
}

View file

@ -20,26 +20,25 @@
#include <LibWeb/Painting/DisplayListRecorder.h>
#include <LibWeb/Painting/PaintableWithLines.h>
#include <LibWeb/Painting/ShadowPainting.h>
#include <LibWeb/Painting/StackingContext.h>
#include <LibWeb/Painting/TextPaintable.h>
#include <LibWeb/Selection/Selection.h>
namespace Web::Painting {
GC_DEFINE_ALLOCATOR(PaintableWithLines);
static void paint_text_decoration(DisplayListRecordingContext&, TextPaintable const&, PaintableFragment::FragmentSpan const&);
static Gfx::Path build_triangle_wave_path(Gfx::IntPoint from, Gfx::IntPoint to, float amplitude);
static void compute_render_spans(PaintableFragment const&, Vector<PaintableFragment::FragmentSpan, 4>&);
static void paint_text_fragment(DisplayListRecordingContext&, PaintableFragment::FragmentSpan const&);
GC::Ref<PaintableWithLines> PaintableWithLines::create(Layout::BlockContainer const& block_container)
NonnullRefPtr<PaintableWithLines> PaintableWithLines::create(Layout::BlockContainer const& block_container)
{
return block_container.heap().allocate<PaintableWithLines>(block_container);
return adopt_ref(*new PaintableWithLines(block_container));
}
GC::Ref<PaintableWithLines> PaintableWithLines::create(Layout::InlineNode const& inline_node, size_t line_index)
NonnullRefPtr<PaintableWithLines> PaintableWithLines::create(Layout::InlineNode const& inline_node, size_t line_index)
{
return inline_node.heap().allocate<PaintableWithLines>(inline_node, line_index);
return adopt_ref(*new PaintableWithLines(inline_node, line_index));
}
PaintableWithLines::PaintableWithLines(Layout::BlockContainer const& layout_box)
@ -288,8 +287,8 @@ void PaintableWithLines::paint(DisplayListRecordingContext& context, PaintPhase
void compute_render_spans(PaintableFragment const& fragment, Vector<PaintableFragment::FragmentSpan, 4>& spans)
{
auto const* text_paintable = as_if<TextPaintable>(fragment.paintable());
if (!text_paintable) {
auto const* maybe_text_paintable = as_if<TextPaintable>(fragment.paintable());
if (!maybe_text_paintable) {
// Non-text fragments still need shadow painting.
spans.append({
.fragment = fragment,
@ -302,11 +301,12 @@ void compute_render_spans(PaintableFragment const& fragment, Vector<PaintableFra
});
return;
}
auto const& text_paintable = *maybe_text_paintable;
if (!text_paintable->is_visible())
if (!text_paintable.is_visible())
return;
auto text_color = text_paintable->computed_values().webkit_text_fill_color();
auto text_color = text_paintable.computed_values().webkit_text_fill_color();
auto selection_offsets = fragment.selection_offsets();
// No selection: single span with base styling.
@ -324,7 +324,7 @@ void compute_render_spans(PaintableFragment const& fragment, Vector<PaintableFra
}
auto [selection_start, selection_end, _] = *selection_offsets;
auto selection_style = text_paintable->selection_style();
auto selection_style = text_paintable.selection_style();
auto selection_text_color = selection_style.text_color.value_or(text_color);
// Convert selection text decoration to fragment text decoration data.

View file

@ -14,13 +14,11 @@
namespace Web::Painting {
class PaintableWithLines : public PaintableBox {
GC_CELL(PaintableWithLines, PaintableBox);
GC_DECLARE_ALLOCATOR(PaintableWithLines);
public:
static GC::Ref<PaintableWithLines> create(Layout::BlockContainer const&);
static GC::Ref<PaintableWithLines> create(Layout::InlineNode const&, size_t line_index);
static NonnullRefPtr<PaintableWithLines> create(Layout::BlockContainer const&);
static NonnullRefPtr<PaintableWithLines> create(Layout::InlineNode const&, size_t line_index);
virtual ~PaintableWithLines() override;
virtual StringView class_name() const override { return "PaintableWithLines"sv; }
virtual void reset_for_relayout() override;
@ -38,13 +36,6 @@ public:
[[nodiscard]] virtual TraversalDecision hit_test(CSSPixelPoint position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const override;
[[nodiscard]] TraversalDecision hit_test_fragments(CSSPixelPoint position, CSSPixelPoint local_position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const;
virtual void visit_edges(Cell::Visitor& visitor) override
{
Base::visit_edges(visitor);
for (auto& fragment : m_fragments)
visitor.visit(GC::Ref { fragment.layout_node() });
}
size_t line_index() const { return m_line_index; }
protected:

View file

@ -15,11 +15,9 @@
namespace Web::Painting {
GC_DEFINE_ALLOCATOR(RadioButtonPaintable);
GC::Ref<RadioButtonPaintable> RadioButtonPaintable::create(Layout::RadioButton const& layout_box)
NonnullRefPtr<RadioButtonPaintable> RadioButtonPaintable::create(Layout::RadioButton const& layout_box)
{
return layout_box.heap().allocate<RadioButtonPaintable>(layout_box);
return adopt_ref(*new RadioButtonPaintable(layout_box));
}
RadioButtonPaintable::RadioButtonPaintable(Layout::RadioButton const& layout_box)

View file

@ -12,11 +12,9 @@
namespace Web::Painting {
class RadioButtonPaintable final : public PaintableBox {
GC_CELL(RadioButtonPaintable, PaintableBox);
GC_DECLARE_ALLOCATOR(RadioButtonPaintable);
public:
static GC::Ref<RadioButtonPaintable> create(Layout::RadioButton const&);
static NonnullRefPtr<RadioButtonPaintable> create(Layout::RadioButton const&);
virtual StringView class_name() const override { return "RadioButtonPaintable"sv; }
virtual void paint(DisplayListRecordingContext&, PaintPhase) const override;

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGC/WeakInlines.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Page/ElementResizeAction.h>
#include <LibWeb/Painting/PaintableBox.h>
@ -14,11 +15,9 @@
namespace Web::Painting {
GC_DEFINE_ALLOCATOR(ResizeHandle);
GC::Ref<ResizeHandle> ResizeHandle::create(GC::Heap& heap, PaintableBox& paintable_box)
NonnullRefPtr<ResizeHandle> ResizeHandle::create(PaintableBox& paintable_box)
{
return heap.allocate<ResizeHandle>(paintable_box);
return adopt_ref(*new ResizeHandle(paintable_box));
}
ResizeHandle::ResizeHandle(PaintableBox& paintable_box)
@ -27,26 +26,23 @@ ResizeHandle::ResizeHandle(PaintableBox& paintable_box)
{
}
void ResizeHandle::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_paintable_box);
visitor.visit(m_element);
if (m_resize_action)
m_resize_action->visit_edges(visitor);
}
bool ResizeHandle::contains(CSSPixelPoint position, ChromeMetrics const& metrics) const
{
return m_paintable_box->resizer_contains(position, metrics);
auto paintable_box = m_paintable_box.strong_ref();
if (!paintable_box)
return false;
return paintable_box->resizer_contains(position, metrics);
}
Optional<CSS::CursorPredefined> ResizeHandle::cursor() const
{
auto axes = m_paintable_box->physical_resize_axes();
auto paintable_box = m_paintable_box.strong_ref();
if (!paintable_box)
return {};
auto axes = paintable_box->physical_resize_axes();
if (axes.vertical) {
if (axes.horizontal) {
if (m_paintable_box->is_chrome_mirrored())
if (paintable_box->is_chrome_mirrored())
return CSS::CursorPredefined::SwResize;
return CSS::CursorPredefined::SeResize;
}
@ -64,8 +60,14 @@ MouseAction ResizeHandle::handle_pointer_event(FlyString const& type, unsigned b
return MouseAction::None;
}
auto element = m_element.ptr();
if (!element || !element->is_connected()) {
m_resize_action.clear();
return MouseAction::None;
}
if (!m_resize_action)
m_resize_action = make<ElementResizeAction>(m_element, visual_viewport_position);
m_resize_action = make<ElementResizeAction>(*element, visual_viewport_position);
else
m_resize_action->handle_pointer_move(visual_viewport_position);

View file

@ -6,17 +6,15 @@
#pragma once
#include <LibGC/Weak.h>
#include <LibWeb/Painting/ChromeWidget.h>
#include <LibWeb/Painting/PaintableBox.h>
namespace Web::Painting {
class ResizeHandle final : public ChromeWidget {
GC_CELL(ResizeHandle, ChromeWidget);
GC_DECLARE_ALLOCATOR(ResizeHandle);
public:
static GC::Ref<ResizeHandle> create(GC::Heap&, PaintableBox&);
static NonnullRefPtr<ResizeHandle> create(PaintableBox&);
bool contains(CSSPixelPoint position, ChromeMetrics const&) const;
@ -29,10 +27,8 @@ public:
private:
ResizeHandle(PaintableBox&);
virtual void visit_edges(Cell::Visitor&) override;
GC::Ref<PaintableBox> m_paintable_box;
GC::Ref<DOM::Element> m_element;
WeakPtr<PaintableBox> m_paintable_box;
GC::Weak<DOM::Element> m_element;
OwnPtr<ElementResizeAction> m_resize_action;
};

Some files were not shown because too many files have changed in this diff Show more