Serenity Operating System
at hosted 383 lines 10 kB view raw
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}