mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 04:57:44 +00:00
LibWeb: Avoid layout invalidation for some CSS property changes
Use the new CSS::property_affects_layout() helper to figure out if we actually need to perform a full relayout after recomputing style. There are three tiers of required invalidation after an element receives new style: none, repaint only, or full relayout. This avoids the need to rebuild the layout tree (and perform layout on it) when trivial properties like "color" etc are changed.
This commit is contained in:
parent
275db39c94
commit
f1711a562a
3 changed files with 60 additions and 10 deletions
|
@ -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<Element>(node))
|
||||
static_cast<Element&>(node).recompute_style();
|
||||
bool needs_relayout = false;
|
||||
|
||||
if (is<Element>(node)) {
|
||||
needs_relayout |= static_cast<Element&>(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<DOM::Element&>(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)
|
||||
|
|
|
@ -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<CSS::PropertyID>(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<CSS::StyleProperties> Element::resolved_css_values()
|
||||
|
|
|
@ -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<Layout::NodeWithStyle*>(Node::layout_node()); }
|
||||
const Layout::NodeWithStyle* layout_node() const { return static_cast<const Layout::NodeWithStyle*>(Node::layout_node()); }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue