Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#pragma once
28
29#include <AK/NonnullRefPtr.h>
30#include <AK/Vector.h>
31#include <LibGfx/FloatRect.h>
32#include <LibGfx/Rect.h>
33#include <LibHTML/CSS/StyleProperties.h>
34#include <LibHTML/Layout/BoxModelMetrics.h>
35#include <LibHTML/Layout/LayoutPosition.h>
36#include <LibHTML/RenderingContext.h>
37#include <LibHTML/TreeNode.h>
38
39class Document;
40class Element;
41class LayoutBlock;
42class LayoutDocument;
43class LayoutNode;
44class LayoutNodeWithStyle;
45class LineBoxFragment;
46class Node;
47
48struct HitTestResult {
49 RefPtr<LayoutNode> layout_node;
50 int index_in_node { 0 };
51};
52
53class LayoutNode : public TreeNode<LayoutNode> {
54public:
55 virtual ~LayoutNode();
56
57 virtual HitTestResult hit_test(const Gfx::Point&) const;
58
59 bool is_anonymous() const { return !m_node; }
60 const Node* node() const { return m_node; }
61
62 Document& document();
63 const Document& document() const;
64
65 const LayoutDocument& root() const;
66 LayoutDocument& root();
67
68 template<typename Callback>
69 inline void for_each_child(Callback callback) const
70 {
71 for (auto* node = first_child(); node; node = node->next_sibling())
72 callback(*node);
73 }
74
75 template<typename Callback>
76 inline void for_each_child(Callback callback)
77 {
78 for (auto* node = first_child(); node; node = node->next_sibling())
79 callback(*node);
80 }
81
82 virtual const char* class_name() const { return "LayoutNode"; }
83 virtual bool is_text() const { return false; }
84 virtual bool is_block() const { return false; }
85 virtual bool is_replaced() const { return false; }
86 virtual bool is_widget() const { return false; }
87 virtual bool is_image() const { return false; }
88 virtual bool is_box() const { return false; }
89 virtual bool is_table() const { return false; }
90 virtual bool is_table_row() const { return false; }
91 virtual bool is_table_cell() const { return false; }
92 bool has_style() const { return m_has_style; }
93
94 bool is_inline() const { return m_inline; }
95 void set_inline(bool b) { m_inline = b; }
96
97 virtual void layout();
98 virtual void render(RenderingContext&);
99
100 const LayoutBlock* containing_block() const;
101
102 virtual LayoutNode& inline_wrapper() { return *this; }
103
104 const StyleProperties& style() const;
105
106 LayoutNodeWithStyle* parent();
107 const LayoutNodeWithStyle* parent() const;
108
109 void inserted_into(LayoutNode&) {}
110 void removed_from(LayoutNode&) {}
111
112 virtual void split_into_lines(LayoutBlock& container);
113
114 bool is_visible() const { return m_visible; }
115 void set_visible(bool visible) { m_visible = visible; }
116
117 virtual void set_needs_display();
118
119 bool children_are_inline() const { return m_children_are_inline; }
120 void set_children_are_inline(bool value) { m_children_are_inline = value; }
121
122 template<typename U>
123 const U* next_sibling_of_type() const;
124
125 template<typename U>
126 U* next_sibling_of_type();
127
128 template<typename T>
129 const T* first_child_of_type() const;
130
131 template<typename T>
132 T* first_child_of_type();
133
134 template<typename T>
135 const T* first_ancestor_of_type() const;
136
137 template<typename T>
138 T* first_ancestor_of_type();
139
140 Gfx::FloatPoint box_type_agnostic_position() const;
141
142protected:
143 explicit LayoutNode(const Node*);
144
145private:
146 friend class LayoutNodeWithStyle;
147
148 const Node* m_node { nullptr };
149
150 bool m_inline { false };
151 bool m_has_style { false };
152 bool m_visible { true };
153 bool m_children_are_inline { false };
154};
155
156class LayoutNodeWithStyle : public LayoutNode {
157public:
158 virtual ~LayoutNodeWithStyle() override {}
159
160 const StyleProperties& style() const { return m_style; }
161 void set_style(const StyleProperties& style) { m_style = style; }
162
163protected:
164 explicit LayoutNodeWithStyle(const Node* node, NonnullRefPtr<StyleProperties> style)
165 : LayoutNode(node)
166 , m_style(move(style))
167 {
168 m_has_style = true;
169 }
170
171private:
172 NonnullRefPtr<StyleProperties> m_style;
173};
174
175class LayoutNodeWithStyleAndBoxModelMetrics : public LayoutNodeWithStyle {
176public:
177 BoxModelMetrics& box_model() { return m_box_model; }
178 const BoxModelMetrics& box_model() const { return m_box_model; }
179
180protected:
181 LayoutNodeWithStyleAndBoxModelMetrics(const Node* node, NonnullRefPtr<StyleProperties> style)
182 : LayoutNodeWithStyle(node, move(style))
183 {
184 }
185
186private:
187 BoxModelMetrics m_box_model;
188};
189
190inline const StyleProperties& LayoutNode::style() const
191{
192 if (m_has_style)
193 return static_cast<const LayoutNodeWithStyle*>(this)->style();
194 return parent()->style();
195}
196
197inline const LayoutNodeWithStyle* LayoutNode::parent() const
198{
199 return static_cast<const LayoutNodeWithStyle*>(TreeNode<LayoutNode>::parent());
200}
201
202inline LayoutNodeWithStyle* LayoutNode::parent()
203{
204 return static_cast<LayoutNodeWithStyle*>(TreeNode<LayoutNode>::parent());
205}
206
207template<typename T>
208inline bool is(const LayoutNode&)
209{
210 return false;
211}
212
213template<typename T>
214inline bool is(const LayoutNode* node)
215{
216 return !node || is<T>(*node);
217}
218
219template<>
220inline bool is<LayoutNode>(const LayoutNode&)
221{
222 return true;
223}
224
225template<>
226inline bool is<LayoutNodeWithStyle>(const LayoutNode& node)
227{
228 return node.has_style();
229}
230
231template<typename T>
232inline const T& to(const LayoutNode& node)
233{
234 ASSERT(is<T>(node));
235 return static_cast<const T&>(node);
236}
237
238template<typename T>
239inline T* to(LayoutNode* node)
240{
241 ASSERT(is<T>(node));
242 return static_cast<T*>(node);
243}
244
245template<typename T>
246inline const T* to(const LayoutNode* node)
247{
248 ASSERT(is<T>(node));
249 return static_cast<const T*>(node);
250}
251
252template<typename T>
253inline T& to(LayoutNode& node)
254{
255 ASSERT(is<T>(node));
256 return static_cast<T&>(node);
257}
258
259template<typename T>
260inline const T* LayoutNode::next_sibling_of_type() const
261{
262 for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) {
263 if (is<T>(*sibling))
264 return &to<T>(*sibling);
265 }
266 return nullptr;
267}
268
269template<typename T>
270inline T* LayoutNode::next_sibling_of_type()
271{
272 for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) {
273 if (is<T>(*sibling))
274 return &to<T>(*sibling);
275 }
276 return nullptr;
277}
278
279template<typename T>
280inline const T* LayoutNode::first_child_of_type() const
281{
282 for (auto* child = first_child(); child; child = child->next_sibling()) {
283 if (is<T>(*child))
284 return &to<T>(*child);
285 }
286 return nullptr;
287}
288
289template<typename T>
290inline T* LayoutNode::first_child_of_type()
291{
292 for (auto* child = first_child(); child; child = child->next_sibling()) {
293 if (is<T>(*child))
294 return &to<T>(*child);
295 }
296 return nullptr;
297}
298
299template<typename T>
300inline const T* LayoutNode::first_ancestor_of_type() const
301{
302 for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
303 if (is<T>(*ancestor))
304 return &to<T>(*ancestor);
305 }
306 return nullptr;
307}
308
309template<typename T>
310inline T* LayoutNode::first_ancestor_of_type()
311{
312 for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
313 if (is<T>(*ancestor))
314 return &to<T>(*ancestor);
315 }
316 return nullptr;
317}