Serenity Operating System
1/*
2 * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#pragma once
8
9#include <AK/NonnullRefPtr.h>
10#include <AK/TypeCasts.h>
11#include <AK/Vector.h>
12#include <LibGfx/Rect.h>
13#include <LibJS/Heap/Cell.h>
14#include <LibJS/Heap/Handle.h>
15#include <LibWeb/CSS/ComputedValues.h>
16#include <LibWeb/CSS/StyleProperties.h>
17#include <LibWeb/Forward.h>
18#include <LibWeb/Layout/BoxModelMetrics.h>
19#include <LibWeb/Painting/PaintContext.h>
20#include <LibWeb/TreeNode.h>
21
22namespace Web::Layout {
23
24enum class LayoutMode {
25 // Normal layout. No min-content or max-content constraints applied.
26 Normal,
27
28 // Intrinsic size determination.
29 // Boxes honor min-content and max-content constraints (set via LayoutState::UsedValues::{width,height}_constraint)
30 // by considering their containing block to be 0-sized or infinitely large in the relevant axis.
31 // https://drafts.csswg.org/css-sizing-3/#intrinsic-sizing
32 IntrinsicSizing,
33};
34
35class Node
36 : public JS::Cell
37 , public TreeNode<Node>
38 , public Weakable<Node> {
39 JS_CELL(Node, JS::Cell);
40
41public:
42 virtual ~Node();
43
44 size_t serial_id() const { return m_serial_id; }
45
46 bool is_anonymous() const;
47 DOM::Node const* dom_node() const;
48 DOM::Node* dom_node();
49
50 bool is_generated() const { return m_generated; }
51 void set_generated(bool b) { m_generated = b; }
52
53 Painting::Paintable* paintable() { return m_paintable; }
54 Painting::Paintable const* paintable() const { return m_paintable; }
55 void set_paintable(JS::GCPtr<Painting::Paintable>);
56
57 virtual JS::GCPtr<Painting::Paintable> create_paintable() const;
58
59 DOM::Document& document();
60 DOM::Document const& document() const;
61
62 HTML::BrowsingContext const& browsing_context() const;
63 HTML::BrowsingContext& browsing_context();
64
65 Viewport const& root() const;
66 Viewport& root();
67
68 bool is_root_element() const;
69
70 DeprecatedString debug_description() const;
71
72 bool has_style() const { return m_has_style; }
73
74 virtual bool can_have_children() const { return true; }
75
76 CSS::Display display() const;
77
78 bool is_inline() const;
79 bool is_inline_block() const;
80 bool is_inline_table() const;
81
82 bool is_out_of_flow(FormattingContext const&) const;
83
84 // These are used to optimize hot is<T> variants for some classes where dynamic_cast is too slow.
85 virtual bool is_box() const { return false; }
86 virtual bool is_block_container() const { return false; }
87 virtual bool is_break_node() const { return false; }
88 virtual bool is_text_node() const { return false; }
89 virtual bool is_viewport() const { return false; }
90 virtual bool is_svg_box() const { return false; }
91 virtual bool is_svg_geometry_box() const { return false; }
92 virtual bool is_svg_svg_box() const { return false; }
93 virtual bool is_label() const { return false; }
94 virtual bool is_replaced_box() const { return false; }
95 virtual bool is_list_item_box() const { return false; }
96 virtual bool is_list_item_marker_box() const { return false; }
97 virtual bool is_table_wrapper() const { return false; }
98 virtual bool is_table() const { return false; }
99 virtual bool is_node_with_style_and_box_model_metrics() const { return false; }
100
101 template<typename T>
102 bool fast_is() const = delete;
103
104 bool is_floating() const;
105 bool is_positioned() const;
106 bool is_absolutely_positioned() const;
107 bool is_fixed_position() const;
108
109 bool is_flex_item() const { return m_is_flex_item; }
110 void set_flex_item(bool b) { m_is_flex_item = b; }
111
112 Box const* containing_block() const;
113 Box* containing_block() { return const_cast<Box*>(const_cast<Node const*>(this)->containing_block()); }
114
115 bool establishes_stacking_context() const;
116
117 bool can_contain_boxes_with_position_absolute() const;
118
119 Gfx::Font const& font() const;
120 CSS::ImmutableComputedValues const& computed_values() const;
121 CSSPixels line_height() const;
122
123 NodeWithStyle* parent();
124 NodeWithStyle const* parent() const;
125
126 void inserted_into(Node&) { }
127 void removed_from(Node&) { }
128 void children_changed() { }
129
130 bool is_visible() const { return m_visible; }
131 void set_visible(bool visible) { m_visible = visible; }
132
133 virtual void set_needs_display();
134
135 bool children_are_inline() const { return m_children_are_inline; }
136 void set_children_are_inline(bool value) { m_children_are_inline = value; }
137
138 CSSPixelPoint box_type_agnostic_position() const;
139
140 enum class SelectionState {
141 None, // No selection
142 Start, // Selection starts in this Node
143 End, // Selection ends in this Node
144 StartAndEnd, // Selection starts and ends in this Node
145 Full, // Selection starts before and ends after this Node
146 };
147
148 SelectionState selection_state() const { return m_selection_state; }
149 void set_selection_state(SelectionState state) { m_selection_state = state; }
150
151protected:
152 Node(DOM::Document&, DOM::Node*);
153
154 virtual void visit_edges(Cell::Visitor&) override;
155
156private:
157 friend class NodeWithStyle;
158
159 JS::NonnullGCPtr<DOM::Node> m_dom_node;
160 JS::GCPtr<Painting::Paintable> m_paintable;
161
162 JS::NonnullGCPtr<HTML::BrowsingContext> m_browsing_context;
163
164 size_t m_serial_id { 0 };
165
166 bool m_anonymous { false };
167 bool m_has_style { false };
168 bool m_visible { true };
169 bool m_children_are_inline { false };
170 SelectionState m_selection_state { SelectionState::None };
171
172 bool m_is_flex_item { false };
173 bool m_generated { false };
174};
175
176class NodeWithStyle : public Node {
177 JS_CELL(NodeWithStyle, Node);
178
179public:
180 virtual ~NodeWithStyle() override = default;
181
182 const CSS::ImmutableComputedValues& computed_values() const { return static_cast<const CSS::ImmutableComputedValues&>(m_computed_values); }
183
184 void apply_style(const CSS::StyleProperties&);
185
186 Gfx::Font const& font() const { return *m_font; }
187 CSSPixels line_height() const { return m_line_height; }
188 Vector<CSS::BackgroundLayerData> const& background_layers() const { return computed_values().background_layers(); }
189 const CSS::AbstractImageStyleValue* list_style_image() const { return m_list_style_image; }
190
191 JS::NonnullGCPtr<NodeWithStyle> create_anonymous_wrapper() const;
192
193 void reset_table_box_computed_values_used_by_wrapper_to_init_values();
194
195protected:
196 NodeWithStyle(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>);
197 NodeWithStyle(DOM::Document&, DOM::Node*, CSS::ComputedValues);
198
199private:
200 CSS::ComputedValues m_computed_values;
201 RefPtr<Gfx::Font const> m_font;
202 CSSPixels m_line_height { 0 };
203 RefPtr<CSS::AbstractImageStyleValue const> m_list_style_image;
204};
205
206class NodeWithStyleAndBoxModelMetrics : public NodeWithStyle {
207 JS_CELL(NodeWithStyleAndBoxModelMetrics, NodeWithStyle);
208
209public:
210 BoxModelMetrics& box_model() { return m_box_model; }
211 BoxModelMetrics const& box_model() const { return m_box_model; }
212
213protected:
214 NodeWithStyleAndBoxModelMetrics(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> style)
215 : NodeWithStyle(document, node, move(style))
216 {
217 }
218
219 NodeWithStyleAndBoxModelMetrics(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values)
220 : NodeWithStyle(document, node, move(computed_values))
221 {
222 }
223
224private:
225 virtual bool is_node_with_style_and_box_model_metrics() const final { return true; }
226
227 BoxModelMetrics m_box_model;
228};
229
230template<>
231inline bool Node::fast_is<NodeWithStyleAndBoxModelMetrics>() const { return is_node_with_style_and_box_model_metrics(); }
232
233inline Gfx::Font const& Node::font() const
234{
235 if (m_has_style)
236 return static_cast<NodeWithStyle const*>(this)->font();
237 return parent()->font();
238}
239
240inline const CSS::ImmutableComputedValues& Node::computed_values() const
241{
242 if (m_has_style)
243 return static_cast<NodeWithStyle const*>(this)->computed_values();
244 return parent()->computed_values();
245}
246
247inline CSSPixels Node::line_height() const
248{
249 if (m_has_style)
250 return static_cast<NodeWithStyle const*>(this)->line_height();
251 return parent()->line_height();
252}
253
254inline NodeWithStyle const* Node::parent() const
255{
256 return static_cast<NodeWithStyle const*>(TreeNode<Node>::parent());
257}
258
259inline NodeWithStyle* Node::parent()
260{
261 return static_cast<NodeWithStyle*>(TreeNode<Node>::parent());
262}
263
264}