#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Document::Document() : ParentNode(*this, NodeType::DOCUMENT_NODE) , m_style_resolver(make(*this)) { m_style_update_timer = CTimer::construct(); m_style_update_timer->set_single_shot(true); m_style_update_timer->set_interval(0); m_style_update_timer->on_timeout = [this] { update_style(); }; } Document::~Document() { } void Document::schedule_style_update() { if (m_style_update_timer->is_active()) return; m_style_update_timer->start(); } bool Document::is_child_allowed(const Node& node) const { switch (node.type()) { case NodeType::DOCUMENT_NODE: case NodeType::TEXT_NODE: return false; case NodeType::COMMENT_NODE: return true; case NodeType::DOCUMENT_TYPE_NODE: return !first_child_of_type(); case NodeType::ELEMENT_NODE: return !first_child_of_type(); default: return false; } } void Document::fixup() { if (!first_child() || !is(*first_child())) prepend_child(adopt(*new DocumentType(*this))); if (is(first_child()->next_sibling())) return; auto body = create_element(*this, "body"); auto html = create_element(*this, "html"); html->append_child(body); this->donate_all_children_to(body); this->append_child(html); } const HTMLHtmlElement* Document::document_element() const { return first_child_of_type(); } const HTMLHeadElement* Document::head() const { auto* html = document_element(); if (!html) return nullptr; return html->first_child_of_type(); } const HTMLBodyElement* Document::body() const { auto* html = document_element(); if (!html) return nullptr; return html->first_child_of_type(); } String Document::title() const { auto* head_element = head(); if (!head_element) return {}; auto* title_element = head_element->first_child_of_type(); if (!title_element) return {}; return title_element->text_content(); } void Document::attach_to_frame(Badge, Frame& frame) { m_frame = frame.make_weak_ptr(); layout(); } void Document::detach_from_frame(Badge, Frame&) { m_layout_root = nullptr; m_frame = nullptr; } Color Document::background_color() const { auto* body_element = body(); if (!body_element) return Color::White; auto* body_layout_node = body_element->layout_node(); if (!body_layout_node) return Color::White; auto background_color = body_layout_node->style().property(CSS::PropertyID::BackgroundColor); if (!background_color.has_value() || !background_color.value()->is_color()) return Color::White; return background_color.value()->to_color(*this); } RefPtr Document::background_image() const { auto* body_element = body(); if (!body_element) return {}; auto* body_layout_node = body_element->layout_node(); if (!body_layout_node) return {}; auto background_image = body_layout_node->style().property(CSS::PropertyID::BackgroundImage); if (!background_image.has_value() || !background_image.value()->is_image()) return {}; auto& image_value = static_cast(*background_image.value()); if (!image_value.bitmap()) return {}; return *image_value.bitmap(); } URL Document::complete_url(const String& string) const { URL url(string); if (url.is_valid()) return url; FileSystemPath fspath(m_url.path()); StringBuilder builder; builder.append('/'); bool document_url_ends_in_slash = m_url.path()[m_url.path().length() - 1] == '/'; for (int i = 0; i < fspath.parts().size(); ++i) { if (i == fspath.parts().size() - 1 && !document_url_ends_in_slash) break; builder.append(fspath.parts()[i]); builder.append('/'); } builder.append(string); auto built = builder.to_string(); fspath = FileSystemPath(built); url = m_url; url.set_path(fspath.string()); return url; } void Document::layout() { if (!m_layout_root) { LayoutTreeBuilder tree_builder; m_layout_root = tree_builder.build(*this); } m_layout_root->layout(); } void Document::update_style() { for_each_in_subtree([&](Node& node) { if (node.needs_style_update()) to(node).recompute_style(); return IterationDecision::Continue; }); update_layout(); } void Document::update_layout() { layout(); if (on_layout_updated) on_layout_updated(); } RefPtr Document::create_layout_node(const StyleProperties*) const { return adopt(*new LayoutDocument(*this, StyleProperties::create())); } void Document::set_link_color(Color color) { m_link_color = color; } void Document::set_active_link_color(Color color) { m_active_link_color = color; } void Document::set_visited_link_color(Color color) { m_visited_link_color = color; } const LayoutDocument* Document::layout_node() const { return static_cast(Node::layout_node()); } void Document::set_hovered_node(Node* node) { if (m_hovered_node == node) return; RefPtr old_hovered_node = move(m_hovered_node); m_hovered_node = node; invalidate_style(); } const Element* Document::get_element_by_id(const String& id) const { const Element* element = nullptr; for_each_in_subtree([&](auto& node) { if (is(node) && to(node).attribute("id") == id) { element = &to(node); return IterationDecision::Break; } return IterationDecision::Continue; }); return element; } Vector Document::get_elements_by_name(const String& name) const { Vector elements; for_each_in_subtree([&](auto& node) { if (is(node) && to(node).attribute("name") == name) elements.append(&to(node)); return IterationDecision::Continue; }); return elements; }