diff --git a/Libraries/LibHTML/CSS/StyleProperties.cpp b/Libraries/LibHTML/CSS/StyleProperties.cpp
index e0386e63b0..ff3528f7d7 100644
--- a/Libraries/LibHTML/CSS/StyleProperties.cpp
+++ b/Libraries/LibHTML/CSS/StyleProperties.cpp
@@ -97,3 +97,23 @@ int StyleProperties::line_height() const
// FIXME: Allow overriding the line-height. We currently default to 140% which seems to look nice.
return (int)(font().glyph_height() * 1.4f);
}
+
+bool StyleProperties::operator==(const StyleProperties& other) const
+{
+ if (m_property_values.size() != other.m_property_values.size())
+ return false;
+
+ for (auto& it : m_property_values) {
+ auto jt = other.m_property_values.find(it.key);
+ if (jt == other.m_property_values.end())
+ return false;
+ auto& my_value = *it.value;
+ auto& other_value = *jt->value;
+ if (my_value.type() != other_value.type())
+ return false;
+ if (my_value.to_string() != other_value.to_string())
+ return false;
+ }
+
+ return true;
+}
diff --git a/Libraries/LibHTML/CSS/StyleProperties.h b/Libraries/LibHTML/CSS/StyleProperties.h
index c18b87ef93..4f0221bd39 100644
--- a/Libraries/LibHTML/CSS/StyleProperties.h
+++ b/Libraries/LibHTML/CSS/StyleProperties.h
@@ -34,6 +34,9 @@ public:
int line_height() const;
+ bool operator==(const StyleProperties&) const;
+ bool operator!=(const StyleProperties& other) const { return !(*this == other); }
+
private:
HashMap> m_property_values;
diff --git a/Libraries/LibHTML/DOM/Document.cpp b/Libraries/LibHTML/DOM/Document.cpp
index 48bbeec6e5..cd3c97715b 100644
--- a/Libraries/LibHTML/DOM/Document.cpp
+++ b/Libraries/LibHTML/DOM/Document.cpp
@@ -201,7 +201,12 @@ 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;
- update_style();
+
+ if (old_hovered_node)
+ old_hovered_node->invalidate_style();
+ if (m_hovered_node)
+ m_hovered_node->invalidate_style();
}
diff --git a/Libraries/LibHTML/DOM/Element.cpp b/Libraries/LibHTML/DOM/Element.cpp
index 2627e628f8..67b00abcb1 100644
--- a/Libraries/LibHTML/DOM/Element.cpp
+++ b/Libraries/LibHTML/DOM/Element.cpp
@@ -1,4 +1,5 @@
#include
+#include
#include
#include
#include
@@ -92,3 +93,48 @@ RefPtr Element::create_layout_node(const StyleResolver& resolver, co
void Element::parse_attribute(const String&, const String&)
{
}
+
+enum class StyleDifference {
+ None,
+ NeedsRepaint,
+ NeedsRelayout,
+};
+
+static StyleDifference compute_style_difference(const StyleProperties& old_style, const StyleProperties& new_style, const Document& document)
+{
+ if (old_style == new_style)
+ return StyleDifference::None;
+
+ bool needs_repaint = false;
+ bool needs_relayout = false;
+
+ if (new_style.color_or_fallback(CSS::PropertyID::Color, document, Color::Black) != old_style.color_or_fallback(CSS::PropertyID::Color, document, Color::Black))
+ needs_repaint = true;
+ else if (new_style.color_or_fallback(CSS::PropertyID::BackgroundColor, document, Color::Black) != old_style.color_or_fallback(CSS::PropertyID::BackgroundColor, document, Color::Black))
+ needs_repaint = true;
+
+ if (needs_relayout)
+ return StyleDifference::NeedsRelayout;
+ if (needs_repaint)
+ return StyleDifference::NeedsRepaint;
+ return StyleDifference::None;
+}
+
+void Element::recompute_style()
+{
+ ASSERT(parent());
+ auto* parent_layout_node = parent()->layout_node();
+ ASSERT(parent_layout_node);
+ auto style = document().style_resolver().resolve_style(*this, &parent_layout_node->style());
+ ASSERT(layout_node());
+ auto diff = compute_style_difference(layout_node()->style(), *style, document());
+ if (diff == StyleDifference::None)
+ return;
+ layout_node()->set_style(*style);
+ if (diff == StyleDifference::NeedsRelayout) {
+ ASSERT_NOT_REACHED();
+ }
+ if (diff == StyleDifference::NeedsRepaint) {
+ layout_node()->set_needs_display();
+ }
+}
diff --git a/Libraries/LibHTML/DOM/Element.h b/Libraries/LibHTML/DOM/Element.h
index db3e260ed8..1c354a6af0 100644
--- a/Libraries/LibHTML/DOM/Element.h
+++ b/Libraries/LibHTML/DOM/Element.h
@@ -2,6 +2,9 @@
#include
#include
+#include
+
+class LayoutNodeWithStyle;
class Attribute {
public:
@@ -45,6 +48,11 @@ public:
virtual void apply_presentational_hints(StyleProperties&) const {}
virtual void parse_attribute(const String& name, const String& value);
+ void recompute_style();
+
+ LayoutNodeWithStyle* layout_node() { return static_cast(Node::layout_node()); }
+ const LayoutNodeWithStyle* layout_node() const { return static_cast(Node::layout_node()); }
+
private:
RefPtr create_layout_node(const StyleResolver&, const StyleProperties* parent_style) const override;
diff --git a/Libraries/LibHTML/DOM/Node.cpp b/Libraries/LibHTML/DOM/Node.cpp
index 14c7a38a29..54857808eb 100644
--- a/Libraries/LibHTML/DOM/Node.cpp
+++ b/Libraries/LibHTML/DOM/Node.cpp
@@ -102,3 +102,11 @@ RefPtr Node::create_layout_node(const StyleResolver&, const StylePro
{
return nullptr;
}
+
+void Node::invalidate_style()
+{
+ for (auto* node = this; node; node = node->parent()) {
+ if (is(*node))
+ to(*node).recompute_style();
+ }
+}
diff --git a/Libraries/LibHTML/DOM/Node.h b/Libraries/LibHTML/DOM/Node.h
index 9efd4bf962..1154d0466d 100644
--- a/Libraries/LibHTML/DOM/Node.h
+++ b/Libraries/LibHTML/DOM/Node.h
@@ -71,6 +71,8 @@ public:
virtual bool is_child_allowed(const Node&) const { return true; }
+ void invalidate_style();
+
protected:
Node(Document&, NodeType);
diff --git a/Libraries/LibHTML/Layout/LayoutNode.h b/Libraries/LibHTML/Layout/LayoutNode.h
index b412e59370..19c4c50127 100644
--- a/Libraries/LibHTML/Layout/LayoutNode.h
+++ b/Libraries/LibHTML/Layout/LayoutNode.h
@@ -111,6 +111,7 @@ public:
virtual ~LayoutNodeWithStyle() override {}
const StyleProperties& style() const { return m_style; }
+ void set_style(const StyleProperties& style) { m_style = style; }
protected:
explicit LayoutNodeWithStyle(const Node* node, NonnullRefPtr style)