diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 0b8f1447e1..42c8397462 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -595,17 +595,20 @@ void Document::update_layout() m_layout_update_timer->stop(); } -static void update_style_recursively(DOM::Node& node) +static bool update_style_recursively(DOM::Node& node) { - if (is(node)) - static_cast(node).recompute_style(); + bool needs_relayout = false; + + if (is(node)) { + needs_relayout |= static_cast(node).recompute_style() == Element::NeedsRelayout::Yes; + } node.set_needs_style_update(false); if (node.child_needs_style_update()) { if (node.is_element()) { if (auto* shadow_root = static_cast(node).shadow_root()) { if (shadow_root->needs_style_update() || shadow_root->child_needs_style_update()) - update_style_recursively(*shadow_root); + needs_relayout |= update_style_recursively(*shadow_root); } } node.for_each_child([&](auto& child) { @@ -616,6 +619,7 @@ static void update_style_recursively(DOM::Node& node) } node.set_child_needs_style_update(false); + return needs_relayout; } void Document::update_style() @@ -624,9 +628,9 @@ void Document::update_style() return; if (!needs_style_update() && !child_needs_style_update()) return; - update_style_recursively(*this); + if (update_style_recursively(*this)) + invalidate_layout(); m_style_update_timer->stop(); - set_needs_layout(); } void Document::set_link_color(Color color) diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index 30bc862768..df9d378059 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -267,18 +267,60 @@ void Element::parse_attribute(const FlyString& name, const String& value) } } -void Element::recompute_style() +enum class RequiredInvalidation { + None, + RepaintOnly, + Relayout, +}; + +static RequiredInvalidation compute_required_invalidation(CSS::StyleProperties const& old_style, CSS::StyleProperties const& new_style) +{ + bool requires_repaint = false; + for (auto i = to_underlying(CSS::first_property_id); i <= to_underlying(CSS::last_property_id); ++i) { + auto property_id = static_cast(i); + auto const& old_value = old_style.properties()[i]; + auto const& new_value = new_style.properties()[i]; + if (!old_value && !new_value) + continue; + if (!old_value || !new_value) + return RequiredInvalidation::Relayout; + if (*old_value == *new_value) + continue; + if (CSS::property_affects_layout(property_id)) + return RequiredInvalidation::Relayout; + requires_repaint = true; + } + if (requires_repaint) + return RequiredInvalidation::RepaintOnly; + return RequiredInvalidation::None; +} + +Element::NeedsRelayout Element::recompute_style() { set_needs_style_update(false); VERIFY(parent()); auto new_computed_css_values = document().style_computer().compute_style(*this); - if (m_computed_css_values && *m_computed_css_values == *new_computed_css_values) - return; + auto required_invalidation = RequiredInvalidation::Relayout; + + if (m_computed_css_values) + required_invalidation = compute_required_invalidation(*m_computed_css_values, *new_computed_css_values); + + if (required_invalidation == RequiredInvalidation::None) + return NeedsRelayout::No; m_computed_css_values = move(new_computed_css_values); + if (required_invalidation == RequiredInvalidation::RepaintOnly && layout_node()) { + layout_node()->apply_style(*m_computed_css_values); + layout_node()->set_needs_display(); + return NeedsRelayout::No; + } + + // FIXME: Get rid of this layout invalidation and let Document take care of it. + // There seems to be some missing invalidation somewhere else that this is papering over. document().invalidate_layout(); + return NeedsRelayout::Yes; } NonnullRefPtr Element::resolved_css_values() diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index 2a40688f5b..5747d7b9c1 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -87,7 +87,11 @@ public: virtual void parse_attribute(const FlyString& name, const String& value); virtual void did_remove_attribute(FlyString const&) { } - void recompute_style(); + enum class NeedsRelayout { + No = 0, + Yes = 1, + }; + NeedsRelayout recompute_style(); Layout::NodeWithStyle* layout_node() { return static_cast(Node::layout_node()); } const Layout::NodeWithStyle* layout_node() const { return static_cast(Node::layout_node()); }