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 <LibGUI/DisplayLink.h>
32#include <LibGUI/MessageBox.h>
33#include <LibJS/Interpreter.h>
34#include <LibJS/Parser.h>
35#include <LibJS/Runtime/Function.h>
36#include <LibJS/Runtime/GlobalObject.h>
37#include <LibWeb/Bindings/DocumentWrapper.h>
38#include <LibWeb/Bindings/WindowObject.h>
39#include <LibWeb/CSS/SelectorEngine.h>
40#include <LibWeb/CSS/StyleResolver.h>
41#include <LibWeb/DOM/Document.h>
42#include <LibWeb/DOM/DocumentType.h>
43#include <LibWeb/DOM/Element.h>
44#include <LibWeb/DOM/ElementFactory.h>
45#include <LibWeb/DOM/HTMLBodyElement.h>
46#include <LibWeb/DOM/HTMLHeadElement.h>
47#include <LibWeb/DOM/HTMLHtmlElement.h>
48#include <LibWeb/DOM/HTMLTitleElement.h>
49#include <LibWeb/DOM/Window.h>
50#include <LibWeb/Dump.h>
51#include <LibWeb/Frame.h>
52#include <LibWeb/HtmlView.h>
53#include <LibWeb/Layout/LayoutDocument.h>
54#include <LibWeb/Layout/LayoutTreeBuilder.h>
55#include <LibWeb/Origin.h>
56#include <LibWeb/Parser/CSSParser.h>
57#include <stdio.h>
58
59namespace Web {
60
61Document::Document(const URL& url)
62 : ParentNode(*this, NodeType::DOCUMENT_NODE)
63 , m_style_resolver(make<StyleResolver>(*this))
64 , m_url(url)
65 , m_window(Window::create_with_document(*this))
66{
67 m_style_update_timer = Core::Timer::create_single_shot(0, [this] {
68 update_style();
69 });
70}
71
72Document::~Document()
73{
74}
75
76Origin Document::origin() const
77{
78 if (!m_url.is_valid())
79 return {};
80 return { m_url.protocol(), m_url.host(), m_url.port() };
81}
82
83void Document::schedule_style_update()
84{
85 if (m_style_update_timer->is_active())
86 return;
87 m_style_update_timer->start();
88}
89
90bool Document::is_child_allowed(const Node& node) const
91{
92 switch (node.type()) {
93 case NodeType::DOCUMENT_NODE:
94 case NodeType::TEXT_NODE:
95 return false;
96 case NodeType::COMMENT_NODE:
97 return true;
98 case NodeType::DOCUMENT_TYPE_NODE:
99 return !first_child_of_type<DocumentType>();
100 case NodeType::ELEMENT_NODE:
101 return !first_child_of_type<Element>();
102 default:
103 return false;
104 }
105}
106
107void Document::fixup()
108{
109 if (!first_child() || !is<DocumentType>(*first_child()))
110 prepend_child(adopt(*new DocumentType(*this)));
111
112 if (is<HTMLHtmlElement>(first_child()->next_sibling()))
113 return;
114
115 auto body = create_element(*this, "body");
116 auto html = create_element(*this, "html");
117 html->append_child(body);
118 this->donate_all_children_to(body);
119 this->append_child(html);
120}
121
122const HTMLHtmlElement* Document::document_element() const
123{
124 return first_child_of_type<HTMLHtmlElement>();
125}
126
127const HTMLHeadElement* Document::head() const
128{
129 auto* html = document_element();
130 if (!html)
131 return nullptr;
132 return html->first_child_of_type<HTMLHeadElement>();
133}
134
135const HTMLBodyElement* Document::body() const
136{
137 auto* html = document_element();
138 if (!html)
139 return nullptr;
140 return html->first_child_of_type<HTMLBodyElement>();
141}
142
143String Document::title() const
144{
145 auto* head_element = head();
146 if (!head_element)
147 return {};
148
149 auto* title_element = head_element->first_child_of_type<HTMLTitleElement>();
150 if (!title_element)
151 return {};
152
153 return title_element->text_content();
154}
155
156void Document::attach_to_frame(Badge<Frame>, Frame& frame)
157{
158 m_frame = frame.make_weak_ptr();
159 layout();
160}
161
162void Document::detach_from_frame(Badge<Frame>, Frame&)
163{
164 m_layout_root = nullptr;
165 m_frame = nullptr;
166}
167
168Color Document::background_color(const Palette& palette) const
169{
170 auto default_color = palette.base();
171 auto* body_element = body();
172 if (!body_element)
173 return default_color;
174
175 auto* body_layout_node = body_element->layout_node();
176 if (!body_layout_node)
177 return default_color;
178
179 auto background_color = body_layout_node->style().property(CSS::PropertyID::BackgroundColor);
180 if (!background_color.has_value() || !background_color.value()->is_color())
181 return default_color;
182
183 return background_color.value()->to_color(*this);
184}
185
186RefPtr<Gfx::Bitmap> Document::background_image() const
187{
188 auto* body_element = body();
189 if (!body_element)
190 return {};
191
192 auto* body_layout_node = body_element->layout_node();
193 if (!body_layout_node)
194 return {};
195
196 auto background_image = body_layout_node->style().property(CSS::PropertyID::BackgroundImage);
197 if (!background_image.has_value() || !background_image.value()->is_image())
198 return {};
199
200 auto& image_value = static_cast<const ImageStyleValue&>(*background_image.value());
201 if (!image_value.bitmap())
202 return {};
203
204 return *image_value.bitmap();
205}
206
207URL Document::complete_url(const String& string) const
208{
209 return m_url.complete_url(string);
210}
211
212void Document::invalidate_layout()
213{
214 m_layout_root = nullptr;
215}
216
217void Document::force_layout()
218{
219 invalidate_layout();
220 layout();
221}
222
223void Document::layout()
224{
225 if (!frame())
226 return;
227
228 if (!m_layout_root) {
229 LayoutTreeBuilder tree_builder;
230 m_layout_root = static_ptr_cast<LayoutDocument>(tree_builder.build(*this));
231 }
232 m_layout_root->layout();
233 m_layout_root->set_needs_display();
234}
235
236void Document::update_style()
237{
238 for_each_in_subtree_of_type<Element>([&](auto& element) {
239 if (element.needs_style_update())
240 element.recompute_style();
241 return IterationDecision::Continue;
242 });
243 update_layout();
244}
245
246void Document::update_layout()
247{
248 if (!frame())
249 return;
250
251 layout();
252 if (on_layout_updated)
253 on_layout_updated();
254}
255
256RefPtr<LayoutNode> Document::create_layout_node(const StyleProperties*) const
257{
258 return adopt(*new LayoutDocument(*this, StyleProperties::create()));
259}
260
261void Document::set_link_color(Color color)
262{
263 m_link_color = color;
264}
265
266void Document::set_active_link_color(Color color)
267{
268 m_active_link_color = color;
269}
270
271void Document::set_visited_link_color(Color color)
272{
273 m_visited_link_color = color;
274}
275
276const LayoutDocument* Document::layout_node() const
277{
278 return static_cast<const LayoutDocument*>(Node::layout_node());
279}
280
281LayoutDocument* Document::layout_node()
282{
283 return static_cast<LayoutDocument*>(Node::layout_node());
284}
285
286void Document::set_inspected_node(Node* node)
287{
288 if (m_inspected_node == node)
289 return;
290
291 if (m_inspected_node && m_inspected_node->layout_node())
292 m_inspected_node->layout_node()->set_needs_display();
293
294 m_inspected_node = node;
295
296 if (m_inspected_node && m_inspected_node->layout_node())
297 m_inspected_node->layout_node()->set_needs_display();
298}
299
300void Document::set_hovered_node(Node* node)
301{
302 if (m_hovered_node == node)
303 return;
304
305 RefPtr<Node> old_hovered_node = move(m_hovered_node);
306 m_hovered_node = node;
307
308 invalidate_style();
309}
310
311Vector<const Element*> Document::get_elements_by_name(const String& name) const
312{
313 Vector<const Element*> elements;
314 for_each_in_subtree_of_type<Element>([&](auto& element) {
315 if (element.attribute("name") == name)
316 elements.append(&element);
317 return IterationDecision::Continue;
318 });
319 return elements;
320}
321
322NonnullRefPtrVector<Element> Document::query_selector_all(const StringView& selector_text)
323{
324 auto selector = parse_selector(selector_text);
325 if (!selector.has_value())
326 return {};
327
328 dump_selector(selector.value());
329
330 NonnullRefPtrVector<Element> elements;
331 for_each_in_subtree_of_type<Element>([&](auto& element) {
332 if (SelectorEngine::matches(selector.value(), element)) {
333 elements.append(element);
334 }
335 return IterationDecision::Continue;
336 });
337
338 return elements;
339}
340
341Color Document::link_color() const
342{
343 if (m_link_color.has_value())
344 return m_link_color.value();
345 if (!frame())
346 return Color::Blue;
347 return frame()->html_view()->palette().link();
348}
349
350Color Document::active_link_color() const
351{
352 if (m_active_link_color.has_value())
353 return m_active_link_color.value();
354 if (!frame())
355 return Color::Red;
356 return frame()->html_view()->palette().active_link();
357}
358
359Color Document::visited_link_color() const
360{
361 if (m_visited_link_color.has_value())
362 return m_visited_link_color.value();
363 if (!frame())
364 return Color::Magenta;
365 return frame()->html_view()->palette().visited_link();
366}
367
368JS::Interpreter& Document::interpreter()
369{
370 if (!m_interpreter)
371 m_interpreter = JS::Interpreter::create<Bindings::WindowObject>(*m_window);
372 return *m_interpreter;
373}
374
375JS::Value Document::run_javascript(const StringView& source)
376{
377 auto program = JS::Parser(JS::Lexer(source)).parse_program();
378 dbg() << "Document::run_javascript('" << source << "') will run:";
379 program->dump(0);
380 return document().interpreter().run(*program);
381}
382
383}