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 <AK/FileSystemPath.h>
28#include <AK/StringBuilder.h>
29#include <LibCore/Timer.h>
30#include <LibGUI/Application.h>
31#include <LibHTML/CSS/StyleResolver.h>
32#include <LibHTML/DOM/Document.h>
33#include <LibHTML/DOM/DocumentType.h>
34#include <LibHTML/DOM/Element.h>
35#include <LibHTML/DOM/ElementFactory.h>
36#include <LibHTML/DOM/HTMLBodyElement.h>
37#include <LibHTML/DOM/HTMLHeadElement.h>
38#include <LibHTML/DOM/HTMLHtmlElement.h>
39#include <LibHTML/DOM/HTMLTitleElement.h>
40#include <LibHTML/Frame.h>
41#include <LibHTML/HtmlView.h>
42#include <LibHTML/Layout/LayoutDocument.h>
43#include <LibHTML/Layout/LayoutTreeBuilder.h>
44#include <stdio.h>
45
46Document::Document()
47 : ParentNode(*this, NodeType::DOCUMENT_NODE)
48 , m_style_resolver(make<StyleResolver>(*this))
49{
50 m_style_update_timer = Core::Timer::construct();
51 m_style_update_timer->set_single_shot(true);
52 m_style_update_timer->set_interval(0);
53 m_style_update_timer->on_timeout = [this] {
54 update_style();
55 };
56}
57
58Document::~Document()
59{
60}
61
62void Document::schedule_style_update()
63{
64 if (m_style_update_timer->is_active())
65 return;
66 m_style_update_timer->start();
67}
68
69bool Document::is_child_allowed(const Node& node) const
70{
71 switch (node.type()) {
72 case NodeType::DOCUMENT_NODE:
73 case NodeType::TEXT_NODE:
74 return false;
75 case NodeType::COMMENT_NODE:
76 return true;
77 case NodeType::DOCUMENT_TYPE_NODE:
78 return !first_child_of_type<DocumentType>();
79 case NodeType::ELEMENT_NODE:
80 return !first_child_of_type<Element>();
81 default:
82 return false;
83 }
84}
85
86void Document::fixup()
87{
88 if (!first_child() || !is<DocumentType>(*first_child()))
89 prepend_child(adopt(*new DocumentType(*this)));
90
91 if (is<HTMLHtmlElement>(first_child()->next_sibling()))
92 return;
93
94 auto body = create_element(*this, "body");
95 auto html = create_element(*this, "html");
96 html->append_child(body);
97 this->donate_all_children_to(body);
98 this->append_child(html);
99}
100
101const HTMLHtmlElement* Document::document_element() const
102{
103 return first_child_of_type<HTMLHtmlElement>();
104}
105
106const HTMLHeadElement* Document::head() const
107{
108 auto* html = document_element();
109 if (!html)
110 return nullptr;
111 return html->first_child_of_type<HTMLHeadElement>();
112}
113
114const HTMLBodyElement* Document::body() const
115{
116 auto* html = document_element();
117 if (!html)
118 return nullptr;
119 return html->first_child_of_type<HTMLBodyElement>();
120}
121
122String Document::title() const
123{
124 auto* head_element = head();
125 if (!head_element)
126 return {};
127
128 auto* title_element = head_element->first_child_of_type<HTMLTitleElement>();
129 if (!title_element)
130 return {};
131
132 return title_element->text_content();
133}
134
135void Document::attach_to_frame(Badge<Frame>, Frame& frame)
136{
137 m_frame = frame.make_weak_ptr();
138 layout();
139}
140
141void Document::detach_from_frame(Badge<Frame>, Frame&)
142{
143 m_layout_root = nullptr;
144 m_frame = nullptr;
145}
146
147Color Document::background_color(const Palette& palette) const
148{
149 auto default_color = palette.base();
150 auto* body_element = body();
151 if (!body_element)
152 return default_color;
153
154 auto* body_layout_node = body_element->layout_node();
155 if (!body_layout_node)
156 return default_color;
157
158 auto background_color = body_layout_node->style().property(CSS::PropertyID::BackgroundColor);
159 if (!background_color.has_value() || !background_color.value()->is_color())
160 return default_color;
161
162 return background_color.value()->to_color(*this);
163}
164
165RefPtr<Gfx::Bitmap> Document::background_image() const
166{
167 auto* body_element = body();
168 if (!body_element)
169 return {};
170
171 auto* body_layout_node = body_element->layout_node();
172 if (!body_layout_node)
173 return {};
174
175 auto background_image = body_layout_node->style().property(CSS::PropertyID::BackgroundImage);
176 if (!background_image.has_value() || !background_image.value()->is_image())
177 return {};
178
179 auto& image_value = static_cast<const ImageStyleValue&>(*background_image.value());
180 if (!image_value.bitmap())
181 return {};
182
183 return *image_value.bitmap();
184}
185
186URL Document::complete_url(const String& string) const
187{
188 return m_url.complete_url(string);
189}
190
191void Document::force_layout()
192{
193 m_layout_root = nullptr;
194 layout();
195}
196
197void Document::layout()
198{
199 if (!m_layout_root) {
200 LayoutTreeBuilder tree_builder;
201 m_layout_root = tree_builder.build(*this);
202 }
203 m_layout_root->layout();
204 m_layout_root->set_needs_display();
205}
206
207void Document::update_style()
208{
209 for_each_in_subtree_of_type<Element>([&](auto& element) {
210 if (element.needs_style_update())
211 element.recompute_style();
212 return IterationDecision::Continue;
213 });
214 update_layout();
215}
216
217void Document::update_layout()
218{
219 if (!frame())
220 return;
221
222 layout();
223 if (on_layout_updated)
224 on_layout_updated();
225}
226
227RefPtr<LayoutNode> Document::create_layout_node(const StyleProperties*) const
228{
229 return adopt(*new LayoutDocument(*this, StyleProperties::create()));
230}
231
232void Document::set_link_color(Color color)
233{
234 m_link_color = color;
235}
236
237void Document::set_active_link_color(Color color)
238{
239 m_active_link_color = color;
240}
241
242void Document::set_visited_link_color(Color color)
243{
244 m_visited_link_color = color;
245}
246
247const LayoutDocument* Document::layout_node() const
248{
249 return static_cast<const LayoutDocument*>(Node::layout_node());
250}
251
252LayoutDocument* Document::layout_node()
253{
254 return static_cast<LayoutDocument*>(Node::layout_node());
255}
256
257void Document::set_inspected_node(Node* node)
258{
259 if (m_inspected_node == node)
260 return;
261
262 if (m_inspected_node && m_inspected_node->layout_node())
263 m_inspected_node->layout_node()->set_needs_display();
264
265 m_inspected_node = node;
266
267 if (m_inspected_node && m_inspected_node->layout_node())
268 m_inspected_node->layout_node()->set_needs_display();
269}
270
271void Document::set_hovered_node(Node* node)
272{
273 if (m_hovered_node == node)
274 return;
275
276 RefPtr<Node> old_hovered_node = move(m_hovered_node);
277 m_hovered_node = node;
278
279 invalidate_style();
280}
281
282const Element* Document::get_element_by_id(const String& id) const
283{
284 const Element* found_element = nullptr;
285 for_each_in_subtree_of_type<Element>([&](auto& element) {
286 if (element.attribute("id") == id) {
287 found_element = &element;
288 return IterationDecision::Break;
289 }
290 return IterationDecision::Continue;
291 });
292 return found_element;
293}
294
295Vector<const Element*> Document::get_elements_by_name(const String& name) const
296{
297 Vector<const Element*> elements;
298 for_each_in_subtree_of_type<Element>([&](auto& element) {
299 if (element.attribute("name") == name)
300 elements.append(&element);
301 return IterationDecision::Continue;
302 });
303 return elements;
304}
305
306Color Document::link_color() const
307{
308 if (m_link_color.has_value())
309 return m_link_color.value();
310 if (!frame())
311 return Color::Blue;
312 return frame()->html_view()->palette().link();
313}
314
315Color Document::active_link_color() const
316{
317 if (m_active_link_color.has_value())
318 return m_active_link_color.value();
319 if (!frame())
320 return Color::Red;
321 return frame()->html_view()->palette().active_link();
322}
323
324Color Document::visited_link_color() const
325{
326 if (m_visited_link_color.has_value())
327 return m_visited_link_color.value();
328 if (!frame())
329 return Color::Magenta;
330 return frame()->html_view()->palette().visited_link();
331}