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)