ladybird/Libraries/LibHTML/DOM/Node.cpp
Andreas Kling 735f02900b LibHTML: Implement basic partial style invalidation
This patch makes it possible to call Node::invalidate_style() and have
that node and all of its ancestors recompute their style.

We then figure out if the new style is visually different from the old
style, and if so do a paint invalidation with set_needs_display().
Note that the "are they visually different" code is very incomplete!

Use this to make hover effects a lot more efficient. They no longer
cause a full relayout+repaint, but only a style invalidation.
Style invalidations are still quite heavy though, and there's a lot of
room for improvement there. :^)
2019-10-14 18:33:23 +02:00

112 lines
3.2 KiB
C++

#include <AK/StringBuilder.h>
#include <LibHTML/CSS/StyleResolver.h>
#include <LibHTML/DOM/Element.h>
#include <LibHTML/DOM/HTMLAnchorElement.h>
#include <LibHTML/DOM/Node.h>
#include <LibHTML/Layout/LayoutBlock.h>
#include <LibHTML/Layout/LayoutDocument.h>
#include <LibHTML/Layout/LayoutInline.h>
#include <LibHTML/Layout/LayoutNode.h>
#include <LibHTML/Layout/LayoutText.h>
Node::Node(Document& document, NodeType type)
: m_document(document)
, m_type(type)
{
}
Node::~Node()
{
}
RefPtr<LayoutNode> Node::create_layout_tree(const StyleResolver& resolver, const StyleProperties* parent_style) const
{
auto layout_node = create_layout_node(resolver, parent_style);
if (!layout_node)
return nullptr;
if (!has_children())
return layout_node;
Vector<RefPtr<LayoutNode>> layout_children;
bool have_inline_children = false;
bool have_block_children = false;
static_cast<const ParentNode&>(*this).for_each_child([&](const Node& child) {
auto layout_child = child.create_layout_tree(resolver, &layout_node->style());
if (!layout_child)
return;
if (!layout_child->is_block())
have_inline_children = true;
if (layout_child->is_block())
have_block_children = true;
layout_children.append(move(layout_child));
});
for (auto layout_child : layout_children)
if (have_block_children && have_inline_children && !layout_child->is_block()) {
if (layout_child->is_text() && static_cast<const LayoutText&>(*layout_child).text_for_style(*parent_style) == " ")
continue;
layout_node->inline_wrapper().append_child(*layout_child);
} else {
layout_node->append_child(*layout_child);
}
return layout_node;
}
const HTMLAnchorElement* Node::enclosing_link_element() const
{
return first_ancestor_of_type<HTMLAnchorElement>();
}
const HTMLElement* Node::enclosing_html_element() const
{
return first_ancestor_of_type<HTMLElement>();
}
String Node::text_content() const
{
Vector<String> strings;
StringBuilder builder;
for (auto* child = first_child(); child; child = child->next_sibling()) {
auto text = child->text_content();
if (!text.is_empty()) {
builder.append(child->text_content());
builder.append(' ');
}
}
if (builder.length() > 1)
builder.trim(1);
return builder.to_string();
}
const Element* Node::next_element_sibling() const
{
for (auto* node = next_sibling(); node; node = node->next_sibling()) {
if (node->is_element())
return static_cast<const Element*>(node);
}
return nullptr;
}
const Element* Node::previous_element_sibling() const
{
for (auto* node = previous_sibling(); node; node = node->previous_sibling()) {
if (node->is_element())
return static_cast<const Element*>(node);
}
return nullptr;
}
RefPtr<LayoutNode> Node::create_layout_node(const StyleResolver&, const StyleProperties*) const
{
return nullptr;
}
void Node::invalidate_style()
{
for (auto* node = this; node; node = node->parent()) {
if (is<Element>(*node))
to<Element>(*node).recompute_style();
}
}