Coverage Report

Created: 2025-03-04 07:22

/src/serenity/Userland/Libraries/LibWeb/Painting/Paintable.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2022-2023, Andreas Kling <kling@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <AK/NonnullOwnPtr.h>
10
#include <LibWeb/Layout/Box.h>
11
#include <LibWeb/Layout/LineBox.h>
12
#include <LibWeb/Layout/TextNode.h>
13
14
namespace Web::Painting {
15
16
enum class PaintPhase {
17
    Background,
18
    Border,
19
    TableCollapsedBorder,
20
    Foreground,
21
    Outline,
22
    Overlay,
23
};
24
25
struct HitTestResult {
26
    JS::Handle<Paintable> paintable;
27
    int index_in_node { 0 };
28
    Optional<CSSPixels> vertical_distance {};
29
    Optional<CSSPixels> horizontal_distance {};
30
31
    enum InternalPosition {
32
        None,
33
        Before,
34
        Inside,
35
        After,
36
    };
37
    InternalPosition internal_position { None };
38
39
    DOM::Node* dom_node();
40
    DOM::Node const* dom_node() const;
41
};
42
43
enum class HitTestType {
44
    Exact,      // Exact matches only
45
    TextCursor, // Clicking past the right/bottom edge of text will still hit the text
46
};
47
48
class Paintable
49
    : public JS::Cell
50
    , public TreeNode<Paintable> {
51
    JS_CELL(Paintable, JS::Cell);
52
53
public:
54
    virtual ~Paintable();
55
56
    [[nodiscard]] bool is_visible() const;
57
0
    [[nodiscard]] bool is_positioned() const { return m_positioned; }
58
0
    [[nodiscard]] bool is_fixed_position() const { return m_fixed_position; }
59
0
    [[nodiscard]] bool is_absolutely_positioned() const { return m_absolutely_positioned; }
60
0
    [[nodiscard]] bool is_floating() const { return m_floating; }
61
0
    [[nodiscard]] bool is_inline() const { return m_inline; }
62
0
    [[nodiscard]] bool is_selected() const { return m_selected; }
63
0
    [[nodiscard]] CSS::Display display() const { return layout_node().display(); }
64
65
    template<typename U, typename Callback>
66
    TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback) const
67
0
    {
68
0
        if (is<U>(*this)) {
69
0
            if (auto decision = callback(static_cast<U const&>(*this)); decision != TraversalDecision::Continue)
70
0
                return decision;
71
0
        }
72
0
        for (auto* child = first_child(); child; child = child->next_sibling()) {
73
0
            if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
74
0
                return TraversalDecision::Break;
75
0
        }
76
0
        return TraversalDecision::Continue;
77
0
    }
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_inclusive_subtree_of_type<Web::Painting::PaintableBox, Web::Painting::ViewportPaintable::assign_scroll_frames()::$_0>(Web::Painting::ViewportPaintable::assign_scroll_frames()::$_0) const
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_inclusive_subtree_of_type<Web::Painting::PaintableBox, Web::Painting::ViewportPaintable::assign_clip_frames()::$_0>(Web::Painting::ViewportPaintable::assign_clip_frames()::$_0) const
78
79
    template<typename U, typename Callback>
80
    TraversalDecision for_each_in_subtree_of_type(Callback callback) const
81
0
    {
82
0
        for (auto* child = first_child(); child; child = child->next_sibling()) {
83
0
            if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
84
0
                return TraversalDecision::Break;
85
0
        }
86
0
        return TraversalDecision::Continue;
87
0
    }
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_subtree_of_type<Web::Painting::PaintableBox, Web::Painting::ViewportPaintable::assign_scroll_frames()::$_0>(Web::Painting::ViewportPaintable::assign_scroll_frames()::$_0) const
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_subtree_of_type<Web::Painting::PaintableBox, Web::Painting::ViewportPaintable::assign_clip_frames()::$_0>(Web::Painting::ViewportPaintable::assign_clip_frames()::$_0) const
88
89
    template<typename Callback>
90
    TraversalDecision for_each_in_inclusive_subtree(Callback callback)
91
0
    {
92
0
        if (auto decision = callback(*this); decision != TraversalDecision::Continue)
93
0
            return decision;
94
0
        for (auto* child = first_child(); child; child = child->next_sibling()) {
95
0
            if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
96
0
                return TraversalDecision::Break;
97
0
        }
98
0
        return TraversalDecision::Continue;
99
0
    }
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_inclusive_subtree<Web::Painting::ViewportPaintable::resolve_paint_only_properties()::$_0>(Web::Painting::ViewportPaintable::resolve_paint_only_properties()::$_0)
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_inclusive_subtree<Web::Painting::ViewportPaintable::update_selection()::$_0>(Web::Painting::ViewportPaintable::update_selection()::$_0)
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_inclusive_subtree<Web::Painting::ViewportPaintable::recompute_selection_states(Web::DOM::Range&)::$_0>(Web::Painting::ViewportPaintable::recompute_selection_states(Web::DOM::Range&)::$_0)
100
101
    template<typename Callback>
102
    TraversalDecision for_each_in_inclusive_subtree(Callback callback) const
103
0
    {
104
0
        if (auto decision = callback(*this); decision != TraversalDecision::Continue)
105
0
            return decision;
106
0
        for (auto* child = first_child(); child; child = child->next_sibling()) {
107
0
            if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
108
0
                return TraversalDecision::Break;
109
0
        }
110
0
        return TraversalDecision::Continue;
111
0
    }
Unexecuted instantiation: BackgroundPainting.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_inclusive_subtree<Web::Painting::compute_text_clip_paths(Web::PaintContext&, Web::Painting::Paintable const&)::$_1>(Web::Painting::compute_text_clip_paths(Web::PaintContext&, Web::Painting::Paintable const&)::$_1) const
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_inclusive_subtree<Web::Painting::ViewportPaintable::build_stacking_context_tree()::$_0>(Web::Painting::ViewportPaintable::build_stacking_context_tree()::$_0) const
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_inclusive_subtree<Web::Painting::ViewportPaintable::assign_scroll_frames()::$_1>(Web::Painting::ViewportPaintable::assign_scroll_frames()::$_1) const
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_inclusive_subtree<Web::Painting::ViewportPaintable::assign_clip_frames()::$_1>(Web::Painting::ViewportPaintable::assign_clip_frames()::$_1) const
112
113
    template<typename Callback>
114
    TraversalDecision for_each_in_subtree(Callback callback) const
115
0
    {
116
0
        for (auto* child = first_child(); child; child = child->next_sibling()) {
117
0
            if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
118
0
                return TraversalDecision::Break;
119
0
        }
120
0
        return TraversalDecision::Continue;
121
0
    }
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_subtree<Web::Painting::ViewportPaintable::build_stacking_context_tree()::$_0>(Web::Painting::ViewportPaintable::build_stacking_context_tree()::$_0) const
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_subtree<Web::Painting::ViewportPaintable::assign_scroll_frames()::$_1>(Web::Painting::ViewportPaintable::assign_scroll_frames()::$_1) const
Unexecuted instantiation: ViewportPaintable.cpp:Web::TraversalDecision Web::Painting::Paintable::for_each_in_subtree<Web::Painting::ViewportPaintable::assign_clip_frames()::$_1>(Web::Painting::ViewportPaintable::assign_clip_frames()::$_1) const
122
123
0
    StackingContext* stacking_context() { return m_stacking_context; }
124
0
    StackingContext const* stacking_context() const { return m_stacking_context; }
125
    void set_stacking_context(NonnullOwnPtr<StackingContext>);
126
    StackingContext* enclosing_stacking_context();
127
128
    void invalidate_stacking_context();
129
130
0
    virtual void before_paint(PaintContext&, PaintPhase) const { }
131
0
    virtual void after_paint(PaintContext&, PaintPhase) const { }
132
133
0
    virtual void paint(PaintContext&, PaintPhase) const { }
134
135
0
    virtual void before_children_paint(PaintContext&, PaintPhase) const { }
136
0
    virtual void after_children_paint(PaintContext&, PaintPhase) const { }
137
138
0
    virtual void apply_scroll_offset(PaintContext&, PaintPhase) const { }
139
0
    virtual void reset_scroll_offset(PaintContext&, PaintPhase) const { }
140
141
0
    virtual void apply_clip_overflow_rect(PaintContext&, PaintPhase) const { }
142
0
    virtual void clear_clip_overflow_rect(PaintContext&, PaintPhase) const { }
143
144
    [[nodiscard]] virtual TraversalDecision hit_test(CSSPixelPoint, HitTestType, Function<TraversalDecision(HitTestResult)> const& callback) const;
145
146
0
    virtual bool wants_mouse_events() const { return false; }
147
148
0
    virtual bool forms_unconnected_subtree() const { return false; }
149
150
    enum class DispatchEventOfSameName {
151
        Yes,
152
        No,
153
    };
154
    // When these methods return true, the DOM event with the same name will be
155
    // dispatch at the mouse_event_target if it returns a valid DOM::Node, or
156
    // the layout node's associated DOM node if it doesn't.
157
    virtual DispatchEventOfSameName handle_mousedown(Badge<EventHandler>, CSSPixelPoint, unsigned button, unsigned modifiers);
158
    virtual DispatchEventOfSameName handle_mouseup(Badge<EventHandler>, CSSPixelPoint, unsigned button, unsigned modifiers);
159
    virtual DispatchEventOfSameName handle_mousemove(Badge<EventHandler>, CSSPixelPoint, unsigned buttons, unsigned modifiers);
160
0
    virtual DOM::Node* mouse_event_target() const { return nullptr; }
161
162
    virtual bool handle_mousewheel(Badge<EventHandler>, CSSPixelPoint, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y);
163
164
0
    Layout::Node const& layout_node() const { return m_layout_node; }
165
0
    Layout::Node& layout_node() { return const_cast<Layout::Node&>(*m_layout_node); }
166
167
    [[nodiscard]] JS::GCPtr<DOM::Node> dom_node();
168
    [[nodiscard]] JS::GCPtr<DOM::Node const> dom_node() const;
169
    void set_dom_node(JS::GCPtr<DOM::Node>);
170
171
0
    auto const& computed_values() const { return m_layout_node->computed_values(); }
172
173
0
    bool visible_for_hit_testing() const { return computed_values().pointer_events() != CSS::PointerEvents::None; }
174
175
    [[nodiscard]] HTML::BrowsingContext const& browsing_context() const;
176
    [[nodiscard]] HTML::BrowsingContext& browsing_context();
177
178
    JS::GCPtr<HTML::Navigable> navigable() const;
179
180
    virtual void set_needs_display() const;
181
182
    PaintableBox* containing_block() const
183
0
    {
184
0
        if (!m_containing_block.has_value()) {
185
0
            auto containing_layout_box = m_layout_node->containing_block();
186
0
            if (containing_layout_box)
187
0
                m_containing_block = const_cast<PaintableBox*>(containing_layout_box->paintable_box());
188
0
            else
189
0
                m_containing_block = nullptr;
190
0
        }
191
0
        return *m_containing_block;
192
0
    }
193
194
    template<typename T>
195
    bool fast_is() const = delete;
196
197
0
    [[nodiscard]] virtual bool is_paintable_box() const { return false; }
198
0
    [[nodiscard]] virtual bool is_paintable_with_lines() const { return false; }
199
0
    [[nodiscard]] virtual bool is_inline_paintable() const { return false; }
200
0
    [[nodiscard]] virtual bool is_svg_paintable() const { return false; }
201
0
    [[nodiscard]] virtual bool is_text_paintable() const { return false; }
202
203
0
    DOM::Document const& document() const { return layout_node().document(); }
204
0
    DOM::Document& document() { return layout_node().document(); }
205
206
    CSSPixelPoint box_type_agnostic_position() const;
207
208
    enum class SelectionState : u8 {
209
        None,        // No selection
210
        Start,       // Selection starts in this Node
211
        End,         // Selection ends in this Node
212
        StartAndEnd, // Selection starts and ends in this Node
213
        Full,        // Selection starts before and ends after this Node
214
    };
215
216
0
    SelectionState selection_state() const { return m_selection_state; }
217
0
    void set_selection_state(SelectionState state) { m_selection_state = state; }
218
0
    void set_selected(bool selected) { m_selected = selected; }
219
220
    Gfx::AffineTransform compute_combined_css_transform() const;
221
222
0
    virtual void resolve_paint_properties() {};
223
224
protected:
225
    explicit Paintable(Layout::Node const&);
226
227
    virtual void visit_edges(Cell::Visitor&) override;
228
229
private:
230
    JS::GCPtr<DOM::Node> m_dom_node;
231
    JS::NonnullGCPtr<Layout::Node const> m_layout_node;
232
    JS::NonnullGCPtr<HTML::BrowsingContext> m_browsing_context;
233
    Optional<JS::GCPtr<PaintableBox>> mutable m_containing_block;
234
235
    OwnPtr<StackingContext> m_stacking_context;
236
237
    SelectionState m_selection_state { SelectionState::None };
238
239
    bool m_positioned : 1 { false };
240
    bool m_fixed_position : 1 { false };
241
    bool m_absolutely_positioned : 1 { false };
242
    bool m_floating : 1 { false };
243
    bool m_inline : 1 { false };
244
    bool m_selected : 1 { false };
245
};
246
247
inline DOM::Node* HitTestResult::dom_node()
248
0
{
249
0
    return paintable->dom_node();
250
0
}
251
252
inline DOM::Node const* HitTestResult::dom_node() const
253
0
{
254
0
    return paintable->dom_node();
255
0
}
256
257
template<>
258
0
inline bool Paintable::fast_is<PaintableBox>() const { return is_paintable_box(); }
259
260
template<>
261
0
inline bool Paintable::fast_is<PaintableWithLines>() const { return is_paintable_with_lines(); }
262
263
template<>
264
0
inline bool Paintable::fast_is<TextPaintable>() const { return is_text_paintable(); }
265
266
Painting::BorderRadiiData normalize_border_radii_data(Layout::Node const& node, CSSPixelRect const& rect, CSS::BorderRadiusData const& top_left_radius, CSS::BorderRadiusData const& top_right_radius, CSS::BorderRadiusData const& bottom_right_radius, CSS::BorderRadiusData const& bottom_left_radius);
267
268
}