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#include <LibGUI/Painter.h>
28#include <LibHTML/DOM/Document.h>
29#include <LibHTML/DOM/Element.h>
30#include <LibHTML/Frame.h>
31#include <LibHTML/Layout/LayoutBlock.h>
32#include <LibHTML/Layout/LayoutNode.h>
33
34LayoutNode::LayoutNode(const Node* node)
35 : m_node(node)
36{
37 if (m_node)
38 m_node->set_layout_node({}, this);
39}
40
41LayoutNode::~LayoutNode()
42{
43 if (m_node && m_node->layout_node() == this)
44 m_node->set_layout_node({}, nullptr);
45}
46
47void LayoutNode::layout()
48{
49 for_each_child([](auto& child) {
50 child.layout();
51 });
52}
53
54const LayoutBlock* LayoutNode::containing_block() const
55{
56 for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
57 if (is<LayoutBlock>(*ancestor))
58 return to<LayoutBlock>(ancestor);
59 }
60 return nullptr;
61}
62
63void LayoutNode::render(RenderingContext& context)
64{
65 if (!is_visible())
66 return;
67
68 // TODO: render our border
69 for_each_child([&](auto& child) {
70 child.render(context);
71 });
72}
73
74HitTestResult LayoutNode::hit_test(const Gfx::Point& position) const
75{
76 HitTestResult result;
77 for_each_child([&](auto& child) {
78 auto child_result = child.hit_test(position);
79 if (child_result.layout_node)
80 result = child_result;
81 });
82 return result;
83}
84
85const Document& LayoutNode::document() const
86{
87 if (is_anonymous())
88 return parent()->document();
89 return node()->document();
90}
91
92Document& LayoutNode::document()
93{
94 if (is_anonymous())
95 return parent()->document();
96 // FIXME: Remove this const_cast once we give up on the idea of a const link from layout tree to DOM tree.
97 return const_cast<Node*>(node())->document();
98}
99
100const LayoutDocument& LayoutNode::root() const
101{
102 ASSERT(document().layout_node());
103 return *document().layout_node();
104}
105
106LayoutDocument& LayoutNode::root()
107{
108 ASSERT(document().layout_node());
109 return *document().layout_node();
110}
111
112void LayoutNode::split_into_lines(LayoutBlock& container)
113{
114 for_each_child([&](auto& child) {
115 if (child.is_inline()) {
116 child.split_into_lines(container);
117 } else {
118 // FIXME: Support block children of inlines.
119 }
120 });
121}
122
123void LayoutNode::set_needs_display()
124{
125 auto* frame = document().frame();
126 ASSERT(frame);
127
128 if (auto* block = containing_block()) {
129 block->for_each_fragment([&](auto& fragment) {
130 if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) {
131 const_cast<Frame*>(frame)->set_needs_display(enclosing_int_rect(fragment.rect()));
132 }
133 return IterationDecision::Continue;
134 });
135 }
136}
137
138Gfx::FloatPoint LayoutNode::box_type_agnostic_position() const
139{
140 if (is_box())
141 return to<LayoutBox>(*this).position();
142 ASSERT(is_inline());
143 Gfx::FloatPoint position;
144 if (auto* block = containing_block()) {
145 block->for_each_fragment([&](auto& fragment) {
146 if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) {
147 position = fragment.rect().location();
148 return IterationDecision::Break;
149 }
150 return IterationDecision::Continue;
151 });
152 }
153 return position;
154}