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 <LibWeb/CSS/StyleProperties.h>
34#include <LibWeb/Layout/BoxModelMetrics.h>
35#include <LibWeb/Layout/LayoutPosition.h>
36#include <LibWeb/RenderingContext.h>
37#include <LibWeb/TreeNode.h>
38
39namespace Web {
40
41class Document;
42class Element;
43class LayoutBlock;
44class LayoutDocument;
45class LayoutNode;
46class LayoutNodeWithStyle;
47class LineBoxFragment;
48class Node;
49
50struct HitTestResult {
51 RefPtr<LayoutNode> layout_node;
52 int index_in_node { 0 };
53};
54
55class LayoutNode : public TreeNode<LayoutNode> {
56public:
57 virtual ~LayoutNode();
58
59 virtual HitTestResult hit_test(const Gfx::Point&) const;
60
61 bool is_anonymous() const { return !m_node; }
62 const Node* node() const { return m_node; }
63
64 Document& document();
65 const Document& document() const;
66
67 const LayoutDocument& root() const;
68 LayoutDocument& root();
69
70 template<typename Callback>
71 inline void for_each_child(Callback callback) const
72 {
73 for (auto* node = first_child(); node; node = node->next_sibling())
74 callback(*node);
75 }
76
77 template<typename Callback>
78 inline void for_each_child(Callback callback)
79 {
80 for (auto* node = first_child(); node; node = node->next_sibling())
81 callback(*node);
82 }
83
84 virtual const char* class_name() const { return "LayoutNode"; }
85 virtual bool is_text() const { return false; }
86 virtual bool is_block() const { return false; }
87 virtual bool is_replaced() const { return false; }
88 virtual bool is_widget() const { return false; }
89 virtual bool is_image() const { return false; }
90 virtual bool is_canvas() const { return false; }
91 virtual bool is_box() const { return false; }
92 virtual bool is_table() const { return false; }
93 virtual bool is_table_row() const { return false; }
94 virtual bool is_table_cell() const { return false; }
95 bool has_style() const { return m_has_style; }
96
97 bool is_inline() const { return m_inline; }
98 void set_inline(bool b) { m_inline = b; }
99
100 virtual void layout();
101 virtual void render(RenderingContext&);
102
103 const LayoutBlock* containing_block() const;
104
105 virtual LayoutNode& inline_wrapper() { return *this; }
106
107 const StyleProperties& style() const;
108
109 LayoutNodeWithStyle* parent();
110 const LayoutNodeWithStyle* parent() const;
111
112 void inserted_into(LayoutNode&) {}
113 void removed_from(LayoutNode&) {}
114 void children_changed() {}
115
116 virtual void split_into_lines(LayoutBlock& container);
117
118 bool is_visible() const { return m_visible; }
119 void set_visible(bool visible) { m_visible = visible; }
120
121 virtual void set_needs_display();
122
123 bool children_are_inline() const { return m_children_are_inline; }
124 void set_children_are_inline(bool value) { m_children_are_inline = value; }
125
126 template<typename U>
127 const U* next_sibling_of_type() const;
128
129 template<typename U>
130 U* next_sibling_of_type();
131
132 template<typename T>
133 const T* first_child_of_type() const;
134
135 template<typename T>
136 T* first_child_of_type();
137
138 template<typename T>
139 const T* first_ancestor_of_type() const;
140
141 template<typename T>
142 T* first_ancestor_of_type();
143
144 Gfx::FloatPoint box_type_agnostic_position() const;
145
146protected:
147 explicit LayoutNode(const Node*);
148
149private:
150 friend class LayoutNodeWithStyle;
151
152 const Node* m_node { nullptr };
153
154 bool m_inline { false };
155 bool m_has_style { false };
156 bool m_visible { true };
157 bool m_children_are_inline { false };
158};
159
160class LayoutNodeWithStyle : public LayoutNode {
161public:
162 virtual ~LayoutNodeWithStyle() override {}
163
164 const StyleProperties& style() const { return m_style; }
165 void set_style(const StyleProperties& style) { m_style = style; }
166
167protected:
168 explicit LayoutNodeWithStyle(const Node* node, NonnullRefPtr<StyleProperties> style)
169 : LayoutNode(node)
170 , m_style(move(style))
171 {
172 m_has_style = true;
173 }
174
175private:
176 NonnullRefPtr<StyleProperties> m_style;
177};
178
179class LayoutNodeWithStyleAndBoxModelMetrics : public LayoutNodeWithStyle {
180public:
181 BoxModelMetrics& box_model() { return m_box_model; }
182 const BoxModelMetrics& box_model() const { return m_box_model; }
183
184protected:
185 LayoutNodeWithStyleAndBoxModelMetrics(const Node* node, NonnullRefPtr<StyleProperties> style)
186 : LayoutNodeWithStyle(node, move(style))
187 {
188 }
189
190private:
191 BoxModelMetrics m_box_model;
192};
193
194inline const StyleProperties& LayoutNode::style() const
195{
196 if (m_has_style)
197 return static_cast<const LayoutNodeWithStyle*>(this)->style();
198 return parent()->style();
199}
200
201inline const LayoutNodeWithStyle* LayoutNode::parent() const
202{
203 return static_cast<const LayoutNodeWithStyle*>(TreeNode<LayoutNode>::parent());
204}
205
206inline LayoutNodeWithStyle* LayoutNode::parent()
207{
208 return static_cast<LayoutNodeWithStyle*>(TreeNode<LayoutNode>::parent());
209}
210
211template<typename T>
212inline bool is(const LayoutNode&)
213{
214 return false;
215}
216
217template<typename T>
218inline bool is(const LayoutNode* node)
219{
220 return !node || is<T>(*node);
221}
222
223template<>
224inline bool is<LayoutNode>(const LayoutNode&)
225{
226 return true;
227}
228
229template<>
230inline bool is<LayoutNodeWithStyle>(const LayoutNode& node)
231{
232 return node.has_style();
233}
234
235template<typename T>
236inline const T& to(const LayoutNode& node)
237{
238 ASSERT(is<T>(node));
239 return static_cast<const T&>(node);
240}
241
242template<typename T>
243inline T* to(LayoutNode* node)
244{
245 ASSERT(is<T>(node));
246 return static_cast<T*>(node);
247}
248
249template<typename T>
250inline const T* to(const LayoutNode* node)
251{
252 ASSERT(is<T>(node));
253 return static_cast<const T*>(node);
254}
255
256template<typename T>
257inline T& to(LayoutNode& node)
258{
259 ASSERT(is<T>(node));
260 return static_cast<T&>(node);
261}
262
263template<typename T>
264inline const T* LayoutNode::next_sibling_of_type() const
265{
266 for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) {
267 if (is<T>(*sibling))
268 return &to<T>(*sibling);
269 }
270 return nullptr;
271}
272
273template<typename T>
274inline T* LayoutNode::next_sibling_of_type()
275{
276 for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) {
277 if (is<T>(*sibling))
278 return &to<T>(*sibling);
279 }
280 return nullptr;
281}
282
283template<typename T>
284inline const T* LayoutNode::first_child_of_type() const
285{
286 for (auto* child = first_child(); child; child = child->next_sibling()) {
287 if (is<T>(*child))
288 return &to<T>(*child);
289 }
290 return nullptr;
291}
292
293template<typename T>
294inline T* LayoutNode::first_child_of_type()
295{
296 for (auto* child = first_child(); child; child = child->next_sibling()) {
297 if (is<T>(*child))
298 return &to<T>(*child);
299 }
300 return nullptr;
301}
302
303template<typename T>
304inline const T* LayoutNode::first_ancestor_of_type() const
305{
306 for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
307 if (is<T>(*ancestor))
308 return &to<T>(*ancestor);
309 }
310 return nullptr;
311}
312
313template<typename T>
314inline T* LayoutNode::first_ancestor_of_type()
315{
316 for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
317 if (is<T>(*ancestor))
318 return &to<T>(*ancestor);
319 }
320 return nullptr;
321}
322
323}